diff --git a/.gitignore b/.gitignore
index 1fc3016d3f..5da9566192 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,12 @@ configure.lineno
config.log
config.status
core.*
+*.2010.log
+*.Build.CppClean.log
+*.tlog
+*.unsuccessfulbuild
+*.cache
+*.lastbuildstate
/w32/Library/lastversion
/w32/Library/tmpVersion.Bat
@@ -83,6 +89,7 @@ core.*
/build/config/missing
/build/freeswitch.pc
/build/getlib.sh
+/build/getg729.sh
/build/getsounds.sh
/build/Makefile
/build/Makefile.in
@@ -104,6 +111,7 @@ core.*
/libs/freetdm/testtones
/libs/fsg729-*-installer
/libs/g729/
+/libs/libcodec2/compile
/libs/libcodec2/config.guess
/libs/libcodec2/config.sub
/libs/libcodec2/configure
@@ -118,6 +126,7 @@ core.*
/libs/libcodec2/src/Makefile.in
/libs/libcodec2/unittest/Makefile
/libs/libcodec2/unittest/Makefile.in
+/libs/mpg123-1.13.2/
/scripts/fsxs
/scripts/gentls_cert
@@ -147,3 +156,17 @@ core.*
/src/mod/say/mod_say_th/Makefile
/src/mod/say/mod_say_zh/Makefile
+BuildLog.htm
+/w32/Console/Debug/
+/w32/Console/Release/
+/w32/Library/Debug/
+/w32/Library/Release/
+
+Freeswitch.2010.sdf
+
+/Win32/
+/x64/
+
+src/mod/codecs/mod_celt/*/*/mod_celt.log
+src/mod/endpoints/mod_skinny/*/*/mod_skinny_2010.log
+src/mod/formats/mod_shout/*/*/mod_shout.log
diff --git a/build/modules.conf.in b/build/modules.conf.in
index 63a0ab491e..8171773fe3 100644
--- a/build/modules.conf.in
+++ b/build/modules.conf.in
@@ -36,6 +36,7 @@ applications/mod_valet_parking
#applications/mod_snipe_hunt
#applications/mod_callcenter
#applications/mod_fsk
+#applications/mod_ladspa
codecs/mod_g723_1
codecs/mod_amr
#codecs/mod_amrwb
diff --git a/conf/README_IMPORTANT.txt b/conf/README_IMPORTANT.txt
new file mode 100644
index 0000000000..8d82b404f6
--- /dev/null
+++ b/conf/README_IMPORTANT.txt
@@ -0,0 +1,35 @@
+ -= PLEASE READ THIS BEFORE YOU PUT A FreeSWITCH BOX INTO PRODUCTION =-
+
+This configuration, generally known as the "default configuration" for FreeSWITCH, is *NOT* designed to be put into a production environment without some important modifications. Please keep in mind that the default configuration is designed to demonstrate what FreeSWITCH *can* do, not what it *should* do in your specific scenario.
+
+*** SECURING YOUR SERVER ***
+
+By default, FreeSWITCH starts up and does a NATPMP and UPnP request to your router. If your router supports either of these protocols then FreeSWITCH does two things:
+#1 - It gets the external IP address, which it uses for SIP communications
+#2 - It causes there to be a "pinhole" opened up in the router allowing inbound communications to your FreeSWITCH server
+
+Please re-read #2. Now, please re-read #2 again. If you do not want a pinhole coming through your router then DO NOT USE the "auto-nat" tools. The way to disable the auto-nat (that is, UPnP/NATPMP) checking is to start FreeSWITCH with the "-nonat" flag. Easy enough.
+
+If you are planning on putting a system into production then you had better pay attention to security in other areas as well. If you are behind a firewall then make sure your firewall is actually protecting you. If you have your server on a public-facing Internet connection then we recommend a few things:
+#1 - Consider using iptables (Linux/Unix)
+#2 - Consider using fail2ban (see http://wiki.freeswitch.org/wiki/Fail2ban)
+
+*** SECURING YOUR USERS ***
+
+By default, the static XML files have 20 "directory users" in conf/directory/10xx.xml, numbered 1000-1019. Also, the default dialplan has routing for calls to those same extension numbers. (NOTE: the directory and the dialplan are 100% separate concepts. Check out chapters 3-5 of the awesome FreeSWITCH book for details.)
+
+The default users all have *very* simple passwords for SIP credentials and voicemail. If you put those into a production system then you are either brave, ignorant, or stupid. Please don't be any of those three things! You have a few choices for handling your users:
+
+#1 - Delete the static XML files and use mod_xml_curl for dynamic users from a back-end database
+#2 - Manually edit the static XML user directory files and modify the passwords
+#3 - Run the handy randomize-passwords.pl script found in scripts/perl/ subdirectory under the main FreeSWITCH source directory
+
+*** GETTING HELP ***
+
+FreeSWITCH has a thriving on-line community - we welcome you to join us!
+IRC: #freeswitch on irc.freenode.net
+Mailing List: freeswitch-users on lists.freeswitch.org
+
+You can also get professional FreeSWITCH assistance by visiting http://www.freeswitchsolutions.com or sending an email to consulting@freeswitch.org.
+
+Happy FreeSWITCHing!
diff --git a/conf/autoload_configs/modules.conf.xml b/conf/autoload_configs/modules.conf.xml
index 0e485b24e3..52f054994c 100644
--- a/conf/autoload_configs/modules.conf.xml
+++ b/conf/autoload_configs/modules.conf.xml
@@ -64,6 +64,9 @@
+
+
+
diff --git a/conf/dialplan/default/00_ladspa.xml b/conf/dialplan/default/00_ladspa.xml
new file mode 100644
index 0000000000..a26b193ef5
--- /dev/null
+++ b/conf/dialplan/default/00_ladspa.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/sip_profiles/internal.xml b/conf/sip_profiles/internal.xml
index 426264a1c5..b78ea20070 100644
--- a/conf/sip_profiles/internal.xml
+++ b/conf/sip_profiles/internal.xml
@@ -342,6 +342,13 @@
+
+
+
+
diff --git a/debian/freeswitch-python.install b/debian/freeswitch-python.install
index 2773f0c780..247c26d672 100644
--- a/debian/freeswitch-python.install
+++ b/debian/freeswitch-python.install
@@ -1,3 +1,5 @@
opt/freeswitch/conf/autoload_configs/python.conf.xml
opt/freeswitch/mod/mod_python.so*
usr/lib/python2.*/*-packages/freeswitch.py*
+usr/lib/python2.*/*-packages/ESL.py*
+usr/lib/python2.*/*-packages/_ESL.so*
diff --git a/debian/rules b/debian/rules
index 94aee73a00..0490d546f4 100755
--- a/debian/rules
+++ b/debian/rules
@@ -208,6 +208,7 @@ install: build
dh_installdirs -A
VERBOSE=1 $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
+ cd libs/esl && VERBOSE=1 $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install && cd -
# Build architecture-independent files here.
diff --git a/docs/phrase/phrase_en.xml b/docs/phrase/phrase_en.xml
index 41bc69cde1..36d986bb61 100644
--- a/docs/phrase/phrase_en.xml
+++ b/docs/phrase/phrase_en.xml
@@ -428,6 +428,7 @@
+
diff --git a/libs/.gitignore b/libs/.gitignore
index b31646500e..df284b71b6 100644
--- a/libs/.gitignore
+++ b/libs/.gitignore
@@ -1104,3 +1104,25 @@
/tiff-3.8.2/a.out.dSYM/
/tiff-3.8.2/tiff-Makefile.tgz
/unimrcp/a.out.dSYM/
+/esl/fs_cli.dSYM/
+/esl/ivrd.dSYM/
+/esl/testclient.dSYM/
+/esl/testserver.dSYM/
+
+/openssl-1.0.0a/
+/esl/Debug/
+/esl/Release/
+/freetdm/msvc/Release/
+/freetdm/msvc/Debug/
+
+/spandsp/src/msvc/All/BuildLog make_at_dictionary.htm
+/spandsp/src/msvc/All/BuildLog make_modem_filter.htm
+
+BuildLog*.htm
+/win32/tmp*.bat
+
+/speex/win32/VS2008/libspeex/*/*/libspeex.log
+/speex/win32/VS2008/libspeexdsp/*/*/libspeexdsp.log
+/win32/celt/*/*/libcelt.log
+/win32/libg722_1/*/*/libg722_1.log
+/win32/libshout/*/*/libshout.log
diff --git a/libs/esl/Makefile b/libs/esl/Makefile
index fbc870ac24..da7926d49b 100644
--- a/libs/esl/Makefile
+++ b/libs/esl/Makefile
@@ -16,7 +16,9 @@ SOLINK=-shared -Xlinker -x
# comment the next line to disable c++ (no swig mods for you then)
OBJS += src/esl_oop.o
-all: $(MYLIB) fs_cli testclient testserver ivrd
+all: $(MYLIB) fs_cli testclient testserver ivrd pymod
+
+install: pymod-install
$(MYLIB): $(OBJS) $(HEADERS) $(SRC)
ar rcs $(MYLIB) $(OBJS)
@@ -92,6 +94,9 @@ javamod: $(MYLIB)
managedmod: $(MYLIB)
$(MAKE) MYLIB="../$(MYLIB)" SOLINK="$(SOLINK)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" CXX_CFLAGS="$(CXX_CFLAGS)" -C managed
+perlmod-install: perlmod
+ $(MAKE) -C perl install
+
phpmod-install: phpmod
$(MAKE) -C php install
diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c
index f5975342cc..2985bca2af 100644
--- a/libs/esl/fs_cli.c
+++ b/libs/esl/fs_cli.c
@@ -577,7 +577,7 @@ static int usage(char *name){
printf(" -P, --port=port Port to connect (1 - 65535)\n");
printf(" -u, --user=user@domain user@domain\n");
printf(" -p, --password=password Password\n");
- printf(" -i, --interrupt Allow Control-c to interrupt");
+ printf(" -i, --interrupt Allow Control-c to interrupt\n");
printf(" -x, --execute=command Execute Command and Exit\n");
printf(" -l, --loglevel=command Log Level\n");
printf(" -q, --quiet Disable logging\n");
diff --git a/libs/esl/perl/Makefile b/libs/esl/perl/Makefile
index 058e0d3849..a76e9c5e6d 100644
--- a/libs/esl/perl/Makefile
+++ b/libs/esl/perl/Makefile
@@ -1,4 +1,5 @@
PERL=$(shell which perl)
+PERL_SITEDIR=$(shell perl -MConfig -e 'print $$Config{sitelibexp}')
PERL_LIBDIR=-L$(shell perl -MConfig -e 'print $$Config{archlib}')/CORE
PERL_LIBS=$(shell perl -MConfig -e 'print $$Config{libs}')
LOCAL_CFLAGS=-w -DMULTIPLICITY $(shell $(PERL) -MExtUtils::Embed -e ccopts) -DEMBED_PERL
@@ -30,3 +31,8 @@ swigclean:
reswig: swigclean esl_wrap.cpp perlxsi.c
+install: ESL.so
+ install -m 755 ESL.so $(PERL_SITEDIR)
+ install -m 755 ESL.pm $(PERL_SITEDIR)
+ install -d -m 755 ESL $(PERL_SITEDIR)/ESL
+ install -m 755 ESL/* $(PERL_SITEDIR)/ESL
\ No newline at end of file
diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c
index b0bc311ce0..27000f6071 100644
--- a/libs/esl/src/esl.c
+++ b/libs/esl/src/esl.c
@@ -934,7 +934,6 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
goto fail;
}
- esl_event_safe_destroy(&handle->last_event);
esl_event_safe_destroy(&handle->last_ievent);
if (check_q && handle->race_event) {
@@ -1053,6 +1052,7 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
*save_event = revent;
revent = NULL;
} else {
+ esl_event_safe_destroy(&handle->last_event);
handle->last_event = revent;
}
diff --git a/libs/freetdm/.gitignore b/libs/freetdm/.gitignore
index 2b06b30218..ff2a46a071 100644
--- a/libs/freetdm/.gitignore
+++ b/libs/freetdm/.gitignore
@@ -29,3 +29,8 @@ testtones
!/sample/boost/Makefile
!/sample/dso/Makefile
+freetdm.2010.sdf
+/mod_freetdm/Win32/
+/msvc/Win32/
+/src/ftmod/*/Win32/
+/src/ftmod/*/x64/
diff --git a/libs/freetdm/conf/freetdm.conf b/libs/freetdm/conf/freetdm.conf
index 2f9643dedd..cd269b7736 100644
--- a/libs/freetdm/conf/freetdm.conf
+++ b/libs/freetdm/conf/freetdm.conf
@@ -20,6 +20,9 @@ cpu_reset_alarm_threshold => 70
; cpu_alarm_action => warn,reject
cpu_alarm_action => warn
+; Where to dump DTMF debug files (see per span debugdtmf=yes option)
+debugdtmf_directory=/full/path/to/dtmf/directory
+
; spans are defined with [span ]
; the span type can either be zt, wanpipe or pika
; the span name can be any unique string
diff --git a/libs/freetdm/conf/wanpipe.conf b/libs/freetdm/conf/wanpipe.conf
index ba609ac42e..3784eaf17a 100644
--- a/libs/freetdm/conf/wanpipe.conf
+++ b/libs/freetdm/conf/wanpipe.conf
@@ -1,4 +1,13 @@
[defaults]
+; User space interval at which data will be delivered
codec_ms => 20
+
+; wink and flash interval
wink_ms => 150
flash_ms => 750
+
+; size of the driver queue of elements of MTU size
+; typical case is 10 elements of 80 bytes each (10ms of ulaw/alaw)
+; don't mess with this if you don't know what you're doing
+; txqueue_size => 10
+; rxqueue_size => 10
diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index 0652a8e128..4fd79f8a03 100755
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -3621,8 +3621,9 @@ void dump_chan_xml(ftdm_span_t *span, uint32_t chan_id, switch_stream_handle_t *
"ftdm trace []\n" \
"ftdm notrace []\n" \
"ftdm q931_pcap on|off [pcapfilename without suffix]\n" \
-"ftdm gains []\n" \
+"ftdm gains []\n" \
"ftdm dtmf on|off []\n" \
+"ftdm queuesize []\n" \
"--------------------------------------------------------------------------------\n"
SWITCH_STANDARD_API(ft_function)
{
@@ -4059,7 +4060,7 @@ SWITCH_STANDARD_API(ft_function)
ftdm_channel_t *chan;
ftdm_span_t *span = NULL;
if (argc < 4) {
- stream->write_function(stream, "-ERR Usage: ft gains []\n");
+ stream->write_function(stream, "-ERR Usage: ftdm gains []\n");
goto end;
}
ftdm_span_find_by_name(argv[3], &span);
@@ -4094,6 +4095,50 @@ SWITCH_STANDARD_API(ft_function)
}
}
stream->write_function(stream, "+OK gains set to Rx %f and Tx %f\n", rxgain, txgain);
+ } else if (!strcasecmp(argv[0], "queuesize")) {
+ unsigned int i = 0;
+ uint32_t rxsize = 10;
+ uint32_t txsize = 10;
+ uint32_t chan_id = 0;
+ uint32_t ccount = 0;
+ ftdm_channel_t *chan;
+ ftdm_span_t *span = NULL;
+ if (argc < 4) {
+ stream->write_function(stream, "-ERR Usage: ftdm queuesize []\n");
+ goto end;
+ }
+ ftdm_span_find_by_name(argv[3], &span);
+ if (!span) {
+ stream->write_function(stream, "-ERR invalid span\n");
+ goto end;
+ }
+ if (argc > 4) {
+ chan_id = atoi(argv[4]);
+ if (chan_id > ftdm_span_get_chan_count(span)) {
+ stream->write_function(stream, "-ERR invalid chan\n");
+ goto end;
+ }
+ }
+ i = sscanf(argv[1], "%u", &rxsize);
+ i += sscanf(argv[2], "%u", &txsize);
+ if (i != 2) {
+ stream->write_function(stream, "-ERR invalid queue sizes provided\n");
+ goto end;
+ }
+
+ if (chan_id) {
+ chan = ftdm_span_get_channel(span, chan_id);
+ ftdm_channel_command(chan, FTDM_COMMAND_SET_RX_QUEUE_SIZE, &rxsize);
+ ftdm_channel_command(chan, FTDM_COMMAND_SET_TX_QUEUE_SIZE, &txsize);
+ } else {
+ ccount = ftdm_span_get_chan_count(span);
+ for (i = 1; i < ccount; i++) {
+ chan = ftdm_span_get_channel(span, i);
+ ftdm_channel_command(chan, FTDM_COMMAND_SET_RX_QUEUE_SIZE, &rxsize);
+ ftdm_channel_command(chan, FTDM_COMMAND_SET_TX_QUEUE_SIZE, &txsize);
+ }
+ }
+ stream->write_function(stream, "+OK queue sizes set to Rx %d and Tx %d\n", rxsize, txsize);
} else if (!strcasecmp(argv[0], "restart")) {
uint32_t chan_id = 0;
ftdm_channel_t *chan;
@@ -4243,6 +4288,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_freetdm_load)
switch_console_set_complete("add ftdm notrace");
switch_console_set_complete("add ftdm q931_pcap");
switch_console_set_complete("add ftdm gains");
+ switch_console_set_complete("add ftdm queuesize");
switch_console_set_complete("add ftdm dtmf on");
switch_console_set_complete("add ftdm dtmf off");
switch_console_set_complete("add ftdm core state");
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index ff62dc38bb..96fd235ad2 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -244,6 +244,7 @@ static struct {
ftdm_caller_data_t *call_ids[MAX_CALLIDS+1];
ftdm_mutex_t *call_id_mutex;
uint32_t last_call_id;
+ char dtmfdebug_directory[1024];
} globals;
enum ftdm_enum_cpu_alarm_action_flags
@@ -3458,7 +3459,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
if (!ftdmchan->dtmfdbg.file) {
struct tm currtime;
time_t currsec;
- char dfile[512];
+ char dfile[1024];
currsec = time(NULL);
@@ -3469,10 +3470,18 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
localtime_r(&currsec, &currtime);
#endif
- snprintf(dfile, sizeof(dfile), "dtmf-s%dc%d-20%d-%d-%d-%d:%d:%d.%s",
- ftdmchan->span_id, ftdmchan->chan_id,
- currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
- currtime.tm_hour, currtime.tm_min, currtime.tm_sec, ftdmchan->native_codec == FTDM_CODEC_ULAW ? "ulaw" : ftdmchan->native_codec == FTDM_CODEC_ALAW ? "alaw" : "sln");
+ if (ftdm_strlen_zero(globals.dtmfdebug_directory)) {
+ snprintf(dfile, sizeof(dfile), "dtmf-s%dc%d-20%d-%d-%d-%d:%d:%d.%s",
+ ftdmchan->span_id, ftdmchan->chan_id,
+ currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
+ currtime.tm_hour, currtime.tm_min, currtime.tm_sec, ftdmchan->native_codec == FTDM_CODEC_ULAW ? "ulaw" : ftdmchan->native_codec == FTDM_CODEC_ALAW ? "alaw" : "sln");
+ } else {
+ snprintf(dfile, sizeof(dfile), "%s/dtmf-s%dc%d-20%d-%d-%d-%d:%d:%d.%s",
+ globals.dtmfdebug_directory,
+ ftdmchan->span_id, ftdmchan->chan_id,
+ currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
+ currtime.tm_hour, currtime.tm_min, currtime.tm_sec, ftdmchan->native_codec == FTDM_CODEC_ULAW ? "ulaw" : ftdmchan->native_codec == FTDM_CODEC_ALAW ? "alaw" : "sln");
+ }
ftdmchan->dtmfdbg.file = fopen(dfile, "wb");
if (!ftdmchan->dtmfdbg.file) {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
@@ -4686,6 +4695,9 @@ static ftdm_status_t load_config(void)
globals.cpu_monitor.alarm_action_flags |= FTDM_CPU_ALARM_ACTION_WARN;
}
}
+ } else if (!strncasecmp(var, "debugdtmf_directory", sizeof("debugdtmf_directory")-1)) {
+ ftdm_set_string(globals.dtmfdebug_directory, val);
+ ftdm_log(FTDM_LOG_DEBUG, "Debug DTMF directory set to '%s'\n", globals.dtmfdebug_directory);
} else if (!strncasecmp(var, "cpu_monitoring_interval", sizeof("cpu_monitoring_interval")-1)) {
if (atoi(val) > 0) {
globals.cpu_monitor.interval = atoi(val);
diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
index e051477346..6653a923aa 100644
--- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
+++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c
@@ -943,12 +943,12 @@ static int on_proceeding(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_
if (chan) {
/* Open channel if inband information is available */
- if ((pevent->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) && !ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) {
- ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, opening B-Channel %d:%d\n",
+ if (pevent->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) {
+ ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, B-Channel %d:%d\n",
ftdm_channel_get_span_id(chan),
ftdm_channel_get_id(chan));
- if (ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
+ if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN) && (ftdm_channel_open_chan(chan) != FTDM_SUCCESS)) {
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
ftdm_log(FTDM_LOG_ERROR, "-- Error opening channel %d:%d\n",
@@ -985,12 +985,12 @@ static int on_progress(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_ev
if (chan) {
/* Open channel if inband information is available */
- if ((pevent->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) && !ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) {
- ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, opening B-Channel %d:%d\n",
+ if (pevent->proceeding.progressmask & PRI_PROG_INBAND_AVAILABLE) {
+ ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, B-Channel %d:%d\n",
ftdm_channel_get_span_id(chan),
ftdm_channel_get_id(chan));
- if (ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
+ if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN) && (ftdm_channel_open_chan(chan) != FTDM_SUCCESS)) {
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
ftdm_log(FTDM_LOG_ERROR, "-- Error opening channel %d:%d\n",
@@ -1028,8 +1028,6 @@ static int on_ringing(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_eve
ftdm_channel_t *chan = ftdm_span_get_channel(span, pevent->ringing.channel);
if (chan) {
- ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d\n", ftdm_span_get_id(span), pevent->ringing.channel);
-
/* we may get on_ringing even when we're already in FTDM_CHANNEL_STATE_PROGRESS_MEDIA */
// if (ftdm_channel_get_state(chan) == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
// /* dont try to move to STATE_PROGRESS to avoid annoying veto warning */
@@ -1037,12 +1035,12 @@ static int on_ringing(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_eve
// }
/* Open channel if inband information is available */
- if ((pevent->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE) && !ftdm_test_flag(chan, FTDM_CHANNEL_OPEN)) {
- ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, opening B-Channel %d:%d\n",
+ if ((pevent->ringing.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
+ ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, B-Channel %d:%d\n",
ftdm_channel_get_span_id(chan),
ftdm_channel_get_id(chan));
- if (ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
+ if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN) && (ftdm_channel_open_chan(chan) != FTDM_SUCCESS)) {
ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
ftdm_log(FTDM_LOG_ERROR, "-- Error opening channel %d:%d\n",
@@ -1053,9 +1051,13 @@ static int on_ringing(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_eve
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_TERMINATING);
goto out;
}
+ ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d with media\n", ftdm_span_get_id(span), pevent->proceeding.channel);
+ ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
+ } else {
+// ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_PROGRESS);
+ ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d\n", ftdm_span_get_id(span), pevent->proceeding.channel);
+ ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RINGING);
}
-// ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_PROGRESS);
- ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RINGING);
} else {
ftdm_log(FTDM_LOG_DEBUG, "-- Ringing on channel %d:%d but it's not in the span?\n",
ftdm_span_get_id(span), pevent->ringing.channel);
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj
index 2e7fb82041..01395cb1db 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.2008.vcproj
@@ -118,7 +118,7 @@
- WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
EnableFastChecks
MultiThreadedDLL
@@ -157,7 +157,7 @@
- WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDLL
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
index 9fe28190e1..ee0cdbe1ef 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c
@@ -333,7 +333,8 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_
parse_yesno(var, val, &signal_data->setup_arb);
} else if (!strcasecmp(var, "facility")) {
parse_yesno(var, val, &signal_data->facility);
- } else if (!strcasecmp(var, "min_digits")) {
+ } else if (!strcasecmp(var, "min-digits") ||
+ !strcasecmp(var, "min_digits")) {
signal_data->min_digits = atoi(val);
} else if (!strcasecmp(var, "outbound-called-ton")) {
ftdm_set_ton(val, &span->default_caller_data.dnis.type);
@@ -347,11 +348,11 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_
ftdm_set_ton(val, &span->default_caller_data.rdnis.type);
} else if (!strcasecmp(var, "outbound-rdnis-npi")) {
ftdm_set_npi(val, &span->default_caller_data.rdnis.plan);
- } else if (!strcasecmp(var, "outbound-bearer_cap") ||
- !strcasecmp(var, "outbound-bc-transfer-cap")) {
+ } else if (!strcasecmp(var, "outbound-bc-transfer-cap") ||
+ !strcasecmp(var, "outbound-bearer_cap")) {
ftdm_set_bearer_capability(val, (uint8_t*)&span->default_caller_data.bearer_capability);
- } else if (!strcasecmp(var, "outbound-bearer_layer1") ||
- !strcasecmp(var, "outbound-bc-user-layer1")) {
+ } else if (!strcasecmp(var, "outbound-bc-user-layer1") ||
+ !strcasecmp(var, "outbound-bearer_layer1")) {
ftdm_set_bearer_layer1(val, (uint8_t*)&span->default_caller_data.bearer_layer1);
} else if (!strcasecmp(var, "channel-restart-on-link-up")) {
parse_yesno(var, val, &signal_data->restart_opt);
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
index 791b65f0f6..7b9989dd4c 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c
@@ -677,20 +677,20 @@ void sngisdn_rcv_q921_ind(BdMngmt *status)
}
switch (status->t.usta.alarm.category) {
- case (LCM_CATEGORY_INTERFACE):
- ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n",
- ftdmspan->name,
- DECODE_LCM_CATEGORY(status->t.usta.alarm.category),
- DECODE_LCM_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event,
- DECODE_LCM_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause);
+ case (LCM_CATEGORY_PROTOCOL):
+ ftdm_log(FTDM_LOG_DEBUG, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n",
+ ftdmspan->name,
+ DECODE_LCM_CATEGORY(status->t.usta.alarm.category),
+ DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event,
+ DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause);
break;
default:
ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] %s: %s: %s(%d): %s(%d)\n",
- ftdmspan->name,
- DECODE_LCM_CATEGORY(status->t.usta.alarm.category),
- DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event,
- DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause);
-
+ ftdmspan->name,
+ DECODE_LCM_CATEGORY(status->t.usta.alarm.category),
+ DECODE_LLD_EVENT(status->t.usta.alarm.event), status->t.usta.alarm.event,
+ DECODE_LLD_CAUSE(status->t.usta.alarm.cause), status->t.usta.alarm.cause);
+
switch (status->t.usta.alarm.event) {
case ENTR_CONG: /* Entering Congestion */
ftdm_log(FTDM_LOG_WARNING, "s%d: Entering Congestion\n", ftdmspan->span_id);
diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
index c2e9b5c3a1..8c8c8d5159 100644
--- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
+++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c
@@ -114,7 +114,7 @@ void sngisdn_trace_interpreted_q921(sngisdn_span_data_t *signal_data, ftdm_trace
{
char *data_str = ftdm_calloc(1,200); /* TODO Find a proper size */
sngisdn_decode_q921(data_str, data, data_len);
- ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] s%d FRAME %s:%s\n", signal_data->ftdm_span->name, ftdm_trace_dir2str(dir), data_str);
+ ftdm_log(FTDM_LOG_INFO, "[SNGISDN Q921] %s FRAME %s:%s\n", signal_data->ftdm_span->name, ftdm_trace_dir2str(dir), data_str);
ftdm_safe_free(data_str);
}
diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
index b648daadaf..20a173abb2 100644
--- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
+++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c
@@ -91,6 +91,8 @@ typedef enum {
*/
static struct {
uint32_t codec_ms;
+ uint32_t rxqueue_size;
+ uint32_t txqueue_size;
uint32_t wink_ms;
uint32_t flash_ms;
uint32_t ring_on_ms;
@@ -406,6 +408,20 @@ static FIO_CONFIGURE_FUNCTION(wanpipe_configure)
} else {
wp_globals.codec_ms = num;
}
+ } else if (!strcasecmp(var, "rxqueue_size")) {
+ num = atoi(val);
+ if (num < 1 || num > 1000) {
+ ftdm_log(FTDM_LOG_WARNING, "invalid rx queue size at line %d\n", lineno);
+ } else {
+ wp_globals.rxqueue_size = num;
+ }
+ } else if (!strcasecmp(var, "txqueue_size")) {
+ num = atoi(val);
+ if (num < 1 || num > 1000) {
+ ftdm_log(FTDM_LOG_WARNING, "invalid tx queue size at line %d\n", lineno);
+ } else {
+ wp_globals.txqueue_size = num;
+ }
} else if (!strcasecmp(var, "wink_ms")) {
num = atoi(val);
if (num < 50 || num > 3000) {
@@ -544,6 +560,13 @@ static FIO_OPEN_FUNCTION(wanpipe_open)
ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL);
ftdmchan->effective_interval = ftdmchan->native_interval = wp_globals.codec_ms;
ftdmchan->packet_len = ftdmchan->native_interval * 8;
+
+ if (wp_globals.txqueue_size > 0) {
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_TX_QUEUE_SIZE, &wp_globals.txqueue_size);
+ }
+ if (wp_globals.rxqueue_size > 0) {
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_RX_QUEUE_SIZE, &wp_globals.rxqueue_size);
+ }
}
return FTDM_SUCCESS;
@@ -1315,14 +1338,12 @@ static __inline__ ftdm_status_t wanpipe_channel_process_event(ftdm_channel_t *fc
status = FTDM_BREAK;
} else {
ftdm_status_t status;
- wanpipe_tdm_api_t onhook_tdm_api;
- memset(&onhook_tdm_api, 0, sizeof(onhook_tdm_api));
- status = sangoma_tdm_txsig_onhook(fchan->sockfd, &onhook_tdm_api);
+ status = sangoma_tdm_txsig_onhook(fchan->sockfd, tdm_api);
if (status) {
snprintf(fchan->last_error, sizeof(fchan->last_error), "ONHOOK Failed");
return FTDM_FAIL;
}
- *event_id = onhook_tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_ONHOOK : FTDM_OOB_NOOP;
+ *event_id = tdm_api->wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? FTDM_OOB_ONHOOK : FTDM_OOB_NOOP;
}
}
break;
@@ -1575,6 +1596,9 @@ static FIO_IO_LOAD_FUNCTION(wanpipe_init)
wp_globals.flash_ms = 750;
wp_globals.ring_on_ms = 2000;
wp_globals.ring_off_ms = 4000;
+ /* 0 for queue size will leave driver defaults */
+ wp_globals.txqueue_size = 0;
+ wp_globals.rxqueue_size = 0;
wanpipe_interface.name = "wanpipe";
wanpipe_interface.configure_span = wanpipe_configure_span;
wanpipe_interface.configure = wanpipe_configure;
diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
index 1b55b739fc..9c01b41299 100644
--- a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
+++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c
@@ -1064,6 +1064,20 @@ static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan,
fchan->rx_cas_bits = bits;
}
break;
+ case ZT_EVENT_BADFCS:
+ {
+ ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Bad frame checksum (ZT_EVENT_BADFCS)!\n");
+ /* What else could we do? */
+ *event_id = FTDM_OOB_NOOP;
+ }
+ break;
+ case ZT_EVENT_OVERRUN:
+ {
+ ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Driver overrun! (ZT_EVENT_OVERRUN)\n");
+ /* What else could we do? */
+ *event_id = FTDM_OOB_NOOP;
+ }
+ break;
case ZT_EVENT_NONE:
{
ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "No event\n");
diff --git a/libs/libdingaling/src/libdingaling.c b/libs/libdingaling/src/libdingaling.c
index 44c4589429..6793c3df61 100644
--- a/libs/libdingaling/src/libdingaling.c
+++ b/libs/libdingaling/src/libdingaling.c
@@ -384,9 +384,12 @@ static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from,
}
while(xml) {
- char *type = xtype ? xtype : iks_find_attrib(xml, "type");
+ char *type = NULL;
iks *tag;
-
+
+ if (iks_type(xml)!=IKS_CDATA)
+ type = xtype ? xtype : iks_find_attrib(xml, "type");
+
if (type) {
if (!strcasecmp(type, "redirect")) {
@@ -994,9 +997,9 @@ static int on_commands(void *user_data, ikspak *pak)
uint8_t is_result = strcasecmp(type, "result") ? 0 : 1;
uint8_t is_error = strcasecmp(type, "error") ? 0 : 1;
iks *xml, *xsession, *xerror = NULL, *xredir = NULL;
-
+ struct iks_tag* tmp;
xml = iks_child (pak->x);
-
+ tmp = (struct iks_tag*) xml;
if (is_error) {
if ((xerror = working_find(xml, "error"))) {
char *code = iks_find_attrib(xerror, "code");
diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update
index aacfc996d3..a21313983b 100644
--- a/libs/sofia-sip/.update
+++ b/libs/sofia-sip/.update
@@ -1 +1 @@
-Wed Nov 3 13:53:34 EDT 2010
+Tue Mar 8 12:40:45 CST 2011
diff --git a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
index 13454c1a8e..543f0e135a 100644
--- a/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
+++ b/libs/sofia-sip/libsofia-sip-ua/nta/nta.c
@@ -7781,7 +7781,7 @@ nta_outgoing_t *outgoing_create(nta_agent_t *agent,
if (tpn) {
/* CANCEL or ACK to [3456]XX */
invalid = tport_name_dup(home, orq->orq_tpn, tpn);
-#if HAVE_SOFIA_SRESOLV
+#if 0 //HAVE_SOFIA_SRESOLV
/* We send ACK or CANCEL only if original request was really sent */
assert(tport_name_is_resolved(orq->orq_tpn));
#endif
diff --git a/libs/spandsp/src/t4_rx.c b/libs/spandsp/src/t4_rx.c
index dfdf914804..131bfc0179 100644
--- a/libs/spandsp/src/t4_rx.c
+++ b/libs/spandsp/src/t4_rx.c
@@ -652,6 +652,7 @@ static __inline__ void force_drop_rx_bits(t4_state_t *s, int bits)
static int rx_put_bits(t4_state_t *s, uint32_t bit_string, int quantity)
{
int bits;
+ int old_a0;
/* We decompress bit by bit, as the data stream is received. We need to
scan continuously for EOLs, so we might as well work this way. */
@@ -809,8 +810,23 @@ static int rx_put_bits(t4_state_t *s, uint32_t bit_string, int quantity)
s->t4_t6_rx.a0,
s->t4_t6_rx.b1,
s->t4_t6_rx.run_length);
- s->t4_t6_rx.run_length += (s->t4_t6_rx.b1 - s->t4_t6_rx.a0 + t4_2d_table[bits].param);
+ old_a0 = s->t4_t6_rx.a0;
s->t4_t6_rx.a0 = s->t4_t6_rx.b1 + t4_2d_table[bits].param;
+ /* We need to check if a bad or malicious image is failing to move forward along the row.
+ Going back is obviously bad. We also need to avoid a stall on the spot, except for the
+ special case of the start of the row. Zero movement as the very first element in the
+ row is perfectly normal. */
+ if (s->t4_t6_rx.a0 <= old_a0)
+ {
+ if (s->t4_t6_rx.a0 < old_a0 || s->t4_t6_rx.b_cursor > 1)
+ {
+ /* Undo the update we just started, and carry on as if this code does not exist */
+ /* TODO: we really should record that something wasn't right at this point. */
+ s->t4_t6_rx.a0 = old_a0;
+ break;
+ }
+ }
+ s->t4_t6_rx.run_length += (s->t4_t6_rx.a0 - old_a0);
add_run_to_row(s);
/* We need to move one step in one direction or the other, to change to the
opposite colour */
@@ -832,8 +848,9 @@ static int rx_put_bits(t4_state_t *s, uint32_t bit_string, int quantity)
s->ref_runs[s->t4_t6_rx.b_cursor],
s->ref_runs[s->t4_t6_rx.b_cursor + 1]);
s->t4_t6_rx.b1 += s->ref_runs[s->t4_t6_rx.b_cursor++];
- s->t4_t6_rx.run_length += (s->t4_t6_rx.b1 - s->t4_t6_rx.a0);
+ old_a0 = s->t4_t6_rx.a0;
s->t4_t6_rx.a0 = s->t4_t6_rx.b1;
+ s->t4_t6_rx.run_length += (s->t4_t6_rx.a0 - old_a0);
s->t4_t6_rx.b1 += s->ref_runs[s->t4_t6_rx.b_cursor++];
break;
case S_Ext:
diff --git a/libs/stfu/stfu.c b/libs/stfu/stfu.c
index f00dfafa39..d8797f7cd0 100644
--- a/libs/stfu/stfu.c
+++ b/libs/stfu/stfu.c
@@ -428,6 +428,12 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void
i->ts_drift = ts + (i->ts_offset - timer_ts);
+ if (i->ts_offset && i->ts_drift > 0) {
+ i->ts_offset = timer_ts - ts;
+ i->ts_drift = ts + (i->ts_offset - timer_ts);
+ }
+
+
if (i->max_drift) {
if (i->ts_drift < i->max_drift) {
if (++i->drift_dropped_packets < i->drift_max_dropped) {
@@ -518,8 +524,8 @@ stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, uint32_t pt, void
if (stfu_log != null_logger && i->debug) {
- stfu_log(STFU_LOG_EMERG, "I: %s %u i=%u/%u - g:%u/%u c:%u/%u b:%u - %u:%u - %u %d %u %u %d %d %d/%d\n", i->name,
- i->qlen, i->period_packet_in_count, i->period_time, i->consecutive_good_count,
+ stfu_log(STFU_LOG_EMERG, "I: %s %u/%u i=%u/%u - g:%u/%u c:%u/%u b:%u - %u:%u - %u %d %u %u %d %d %d/%d\n", i->name,
+ i->qlen, i->max_qlen, i->period_packet_in_count, i->period_time, i->consecutive_good_count,
i->decrement_time, i->period_clean_count, i->decrement_time, i->consecutive_bad_count,
ts, ts / i->samples_per_packet,
i->period_missing_count, i->period_need_range_avg,
diff --git a/scripts/perl/FreeSWITCH/Client.pm b/scripts/perl/FreeSWITCH/Client.pm
index 9e97218580..01748a8180 100644
--- a/scripts/perl/FreeSWITCH/Client.pm
+++ b/scripts/perl/FreeSWITCH/Client.pm
@@ -143,7 +143,13 @@ sub sendmsg($$$) {
}
$self->output("\n");
- return $self->readhash($to);
+ for(;;) {
+ $e = $self->readhash(undef);
+ last if $e->{socketerror} or $e->{'content-type'} eq 'command/reply';
+ push @{$self->{events}}, $e;
+ }
+
+ return $e;
}
sub command($$) {
diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h
index 25273bafc4..fdbbc9ab91 100644
--- a/src/include/switch_channel.h
+++ b/src/include/switch_channel.h
@@ -315,6 +315,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_caller_extension_masquerade(switc
*/
SWITCH_DECLARE(void) switch_channel_set_caller_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension);
+SWITCH_DECLARE(void) switch_channel_flip_cid(switch_channel_t *channel);
SWITCH_DECLARE(void) switch_channel_sort_cid(switch_channel_t *channel, switch_bool_t in);
/*!
diff --git a/src/include/switch_types.h b/src/include/switch_types.h
index 4253d0e542..3383a9d88c 100644
--- a/src/include/switch_types.h
+++ b/src/include/switch_types.h
@@ -542,7 +542,7 @@ typedef enum {
SWITCH_RTP_FLAG_BREAK = (1 << 10),
SWITCH_RTP_FLAG_UDPTL = (1 << 11),
SWITCH_RTP_FLAG_DATAWAIT = (1 << 12),
- SWITCH_RTP_FLAG_BUGGY_2833 = (1 << 13),
+ SWITCH_RTP_FLAG_BYTESWAP = (1 << 13),
SWITCH_RTP_FLAG_PASS_RFC2833 = (1 << 14),
SWITCH_RTP_FLAG_AUTO_CNG = (1 << 15),
SWITCH_RTP_FLAG_SECURE_SEND_RESET = (1 << 16),
@@ -1107,6 +1107,8 @@ typedef enum {
CF_DIALPLAN,
CF_BLOCK_BROADCAST_UNTIL_MEDIA,
CF_CNG_PLC,
+ CF_ATTENDED_TRANSFER,
+ CF_LAZY_ATTENDED_TRANSFER,
/* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
CF_FLAG_MAX
} switch_channel_flag_t;
diff --git a/src/mod/.gitignore b/src/mod/.gitignore
index b6e7842a50..b75dbcb9cf 100644
--- a/src/mod/.gitignore
+++ b/src/mod/.gitignore
@@ -31,6 +31,7 @@
/endpoints/mod_portaudio/Makefile.in
/endpoints/mod_skinny/Makefile
/endpoints/mod_skinny/Makefile.in
+/endpoints/mod_skinny/mod_skinny.log
/endpoints/mod_skypopen/Makefile
/endpoints/mod_skypopen/Makefile.in
/endpoints/mod_sofia/Makefile
diff --git a/src/mod/applications/mod_callcenter/mod_callcenter.c b/src/mod/applications/mod_callcenter/mod_callcenter.c
index 12e1960841..b8c5eede74 100644
--- a/src/mod/applications/mod_callcenter/mod_callcenter.c
+++ b/src/mod/applications/mod_callcenter/mod_callcenter.c
@@ -160,13 +160,15 @@ struct cc_member_cancel_reason_table {
typedef enum {
CC_MEMBER_CANCEL_REASON_NONE,
CC_MEMBER_CANCEL_REASON_TIMEOUT,
- CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT
+ CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT,
+ CC_MEMBER_CANCEL_REASON_BREAK_OUT
} cc_member_cancel_reason_t;
static struct cc_member_cancel_reason_table MEMBER_CANCEL_REASON_CHART[] = {
{"NONE", CC_MEMBER_CANCEL_REASON_NONE},
{"TIMEOUT", CC_MEMBER_CANCEL_REASON_TIMEOUT},
{"NO_AGENT_TIMEOUT", CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT},
+ {"BREAK_OUT", CC_MEMBER_CANCEL_REASON_BREAK_OUT},
{NULL, 0}
};
@@ -216,6 +218,7 @@ static char agents_sql[] =
" wrap_up_time INTEGER NOT NULL DEFAULT 0,\n"
" reject_delay_time INTEGER NOT NULL DEFAULT 0,\n"
" busy_delay_time INTEGER NOT NULL DEFAULT 0,\n"
+" no_answer_delay_time INTEGER NOT NULL DEFAULT 0,\n"
" last_bridge_start INTEGER NOT NULL DEFAULT 0,\n"
" last_bridge_end INTEGER NOT NULL DEFAULT 0,\n"
" last_offered_call INTEGER NOT NULL DEFAULT 0,\n"
@@ -705,6 +708,7 @@ static cc_queue_t *load_queue(const char *queue_name)
switch_cache_db_test_reactive(dbh, "select count(ready_time) from agents", NULL, "alter table agents add ready_time integer not null default 0;"
"alter table agents add reject_delay_time integer not null default 0;"
"alter table agents add busy_delay_time integer not null default 0;");
+ switch_cache_db_test_reactive(dbh, "select count(no_answer_delay_time) from agents", NULL, "alter table agents add no_answer_delay_time integer not null default 0;");
switch_cache_db_test_reactive(dbh, "select count(ready_time) from agents", "drop table agents", agents_sql);
switch_cache_db_test_reactive(dbh, "select count(queue) from tiers", "drop table tiers" , tiers_sql);
switch_mutex_init(&queue->mutex, SWITCH_MUTEX_NESTED, queue->pool);
@@ -768,6 +772,7 @@ struct call_helper {
int max_no_answer;
int reject_delay_time;
int busy_delay_time;
+ int no_answer_delay_time;
switch_memory_pool_t *pool;
};
@@ -1007,6 +1012,12 @@ cc_status_t cc_agent_update(const char *key, const char *value, const char *agen
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
+ result = CC_STATUS_SUCCESS;
+ } else if (!strcasecmp(key, "no_answer_delay_time")) {
+ sql = switch_mprintf("UPDATE agents SET no_answer_delay_time = '%ld', system = 'single_box' WHERE name = '%q'", atol(value), agent);
+ cc_execute_sql(NULL, sql, NULL);
+ switch_safe_free(sql);
+
result = CC_STATUS_SUCCESS;
} else if (!strcasecmp(key, "type")) {
if (strcasecmp(value, CC_AGENT_TYPE_CALLBACK) && strcasecmp(value, CC_AGENT_TYPE_UUID_STANDBY)) {
@@ -1203,6 +1214,7 @@ static switch_status_t load_agent(const char *agent_name)
const char *wrap_up_time = switch_xml_attr(x_agent, "wrap-up-time");
const char *reject_delay_time = switch_xml_attr(x_agent, "reject-delay-time");
const char *busy_delay_time = switch_xml_attr(x_agent, "busy-delay-time");
+ const char *no_answer_delay_time = switch_xml_attr(x_agent, "no-answer-delay-time");
if (type) {
cc_status_t res = cc_agent_add(agent_name, type);
@@ -1225,6 +1237,9 @@ static switch_status_t load_agent(const char *agent_name)
if (busy_delay_time) {
cc_agent_update("busy_delay_time", busy_delay_time, agent_name);
}
+ if (no_answer_delay_time) {
+ cc_agent_update("no_answer_delay_time", no_answer_delay_time, agent_name);
+ }
if (type && res == CC_STATUS_AGENT_ALREADY_EXIST) {
cc_agent_update("type", type, agent_name);
@@ -1384,8 +1399,6 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_agent", "%s", h->agent_name);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "cc_agent_type", "%s", h->agent_type);
switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "ignore_early_media", "true");
- /* Force loopback to remain live, if not, the loop will detect the actual channel to gone */
- switch_event_add_header(ovars, SWITCH_STACK_BOTTOM, "loopback_bowout", "false");
t_agent_called = switch_epoch_time_now(NULL);
dialstr = switch_mprintf("%s", h->originate_string);
@@ -1430,9 +1443,36 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
const char *agent_uuid = switch_core_session_get_uuid(agent_session);
switch_channel_t *member_channel = switch_core_session_get_channel(member_session);
switch_channel_t *agent_channel = switch_core_session_get_channel(agent_session);
+ const char *other_loopback_leg_uuid = switch_channel_get_variable(agent_channel, "other_loopback_leg_uuid");
switch_channel_set_variable(agent_channel, "cc_member_pre_answer_uuid", NULL);
+ /* Loopback special case */
+ if (other_loopback_leg_uuid) {
+ switch_core_session_t *other_loopback_session = switch_core_session_locate(other_loopback_leg_uuid);
+ if (other_loopback_session) {
+ switch_channel_t *other_loopback_channel = switch_core_session_get_channel(other_loopback_session);
+ const char *real_uuid = switch_channel_get_variable(other_loopback_channel, SWITCH_SIGNAL_BOND_VARIABLE);
+
+ switch_channel_set_variable(other_loopback_channel, "cc_member_pre_answer_uuid", NULL);
+
+ /* Switch the agent session */
+ if (real_uuid) {
+ switch_core_session_rwunlock(agent_session);
+ agent_uuid = real_uuid;
+ agent_session = switch_core_session_locate(agent_uuid);
+ agent_channel = switch_core_session_get_channel(agent_session);
+
+ switch_channel_set_variable(agent_channel, "cc_queue", h->queue_name);
+ switch_channel_set_variable(agent_channel, "cc_agent", h->agent_name);
+ switch_channel_set_variable(agent_channel, "cc_agent_type", h->agent_type);
+ switch_channel_set_variable(agent_channel, "cc_member_uuid", h->member_uuid);
+ }
+ switch_core_session_rwunlock(other_loopback_session);
+ }
+ }
+
+
if (!strcasecmp(h->queue_strategy,"ring-all")) {
char res[256];
/* Map the Agent to the member */
@@ -1466,6 +1506,9 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-UUID", agent_uuid);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Called-Time", "%ld", (long) t_agent_called);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Answered-Time", "%ld", (long) t_agent_answered);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Joined-Time", "%ld", (long) t_member_called);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", h->member_uuid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", h->member_caller_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", h->member_caller_number);
@@ -1508,15 +1551,20 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
while(switch_channel_up(member_channel) && switch_channel_up(agent_channel) && globals.running) {
switch_yield(100000);
}
- tiers_state = CC_TIER_STATE_READY;
+ tiers_state = CC_TIER_STATE_READY;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(agent_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "bridge-agent-end");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Hangup-Cause", switch_channel_cause2str(cause));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-UUID", agent_uuid);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Called-Time", "%ld", (long) t_agent_called);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Answered-Time", "%ld", (long) t_agent_answered);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Joined-Time", "%ld", (long) t_member_called);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Bridge-Terminated-Time", "%ld", (long) switch_epoch_time_now(NULL));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", h->member_uuid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", h->member_caller_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", h->member_caller_number);
@@ -1542,23 +1590,25 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
switch_channel_event_set_data(member_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "member-queue-end");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Hangup-Cause", switch_channel_cause2str(cause));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", "Terminated");
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Answer-Time", "%ld", (long) (t_agent_answered - t_agent_called));
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Wait-Time", "%ld", (long) (t_agent_answered - t_member_called));
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Talk-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_agent_answered));
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Total-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_member_called));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-UUID", agent_uuid);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Called-Time", "%ld", (long) t_agent_called);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Agent-Answered-Time", "%ld", (long) t_agent_answered);
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Leaving-Time", "%ld", (long) switch_epoch_time_now(NULL));
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Joined-Time", "%ld", (long) t_member_called);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", h->member_uuid);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name",
- switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number",
- switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", h->member_caller_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", h->member_caller_number);
switch_event_fire(&event);
}
} else {
- int delay_next_agent_call = 0;
/* Agent didn't answer or originate failed */
+ int delay_next_agent_call = 0;
sql = switch_mprintf("UPDATE members SET state = '%q', serving_agent = '', serving_system = ''"
" WHERE serving_agent = '%q' AND serving_system = '%q' AND uuid = '%q' AND system = 'single_box'",
cc_member_state2str(CC_MEMBER_STATE_WAITING),
@@ -1566,33 +1616,25 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s Origination Canceled : %s\n",h->agent_name, switch_channel_cause2str(cause));
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s Origination Canceled : %s\n", h->agent_name, switch_channel_cause2str(cause));
switch (cause) {
- case SWITCH_CAUSE_USER_NOT_REGISTERED: /* When we are calling a unregistred device */
- case SWITCH_CAUSE_USER_BUSY: /* Could be the phone is in Do Not Disturb */
- delay_next_agent_call = (h->busy_delay_time > delay_next_agent_call?h->busy_delay_time:delay_next_agent_call);
- break;
- case SWITCH_CAUSE_CALL_REJECTED: /* User could have press the reject call on their phone */
- delay_next_agent_call = (h->reject_delay_time > delay_next_agent_call?h->reject_delay_time:delay_next_agent_call);
- break;
- default:
- break;
- }
-
- switch (cause) {
- case SWITCH_CAUSE_USER_NOT_REGISTERED: /* When we are calling a unregistred device */
- case SWITCH_CAUSE_USER_BUSY: /* Could be the phone is in Do Not Disturb */
- case SWITCH_CAUSE_CALL_REJECTED: /* User could have press the reject call on their phone */
+ /* When we hang-up agents that did not answer in ring-all strategy */
case SWITCH_CAUSE_ORIGINATOR_CANCEL:
- if (delay_next_agent_call > 0) {
- char ready_epoch[64];
- switch_snprintf(ready_epoch, sizeof(ready_epoch), "%" SWITCH_TIME_T_FMT, switch_epoch_time_now(NULL) + delay_next_agent_call); /* Make the time configurable */
- cc_agent_update("ready_time", ready_epoch , h->agent_name);
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s Sleeping for %d secondes\n", h->agent_name, delay_next_agent_call);
- }
break;
+ /* Busy: Do Not Disturb, Circuit congestion */
+ case SWITCH_CAUSE_NORMAL_CIRCUIT_CONGESTION:
+ case SWITCH_CAUSE_USER_BUSY:
+ delay_next_agent_call = (h->busy_delay_time > delay_next_agent_call? h->busy_delay_time : delay_next_agent_call);
+ break;
+ /* Reject: User rejected the call */
+ case SWITCH_CAUSE_CALL_REJECTED:
+ delay_next_agent_call = (h->reject_delay_time > delay_next_agent_call? h->reject_delay_time : delay_next_agent_call);
+ break;
+ /* No answer: Destination does not answer for some other reason */
default:
+ delay_next_agent_call = (h->no_answer_delay_time > delay_next_agent_call? h->no_answer_delay_time : delay_next_agent_call);
+
tiers_state = CC_TIER_STATE_NO_ANSWER;
/* Update Agent NO Answer count */
@@ -1607,6 +1649,28 @@ static void *SWITCH_THREAD_FUNC outbound_agent_thread_run(switch_thread_t *threa
h->agent_name, h->max_no_answer);
cc_agent_update("status", cc_agent_status2str(CC_AGENT_STATUS_ON_BREAK), h->agent_name);
}
+ break;
+ }
+
+ /* Put agent to sleep for some time if necessary */
+ if (delay_next_agent_call > 0) {
+ char ready_epoch[64];
+ switch_snprintf(ready_epoch, sizeof(ready_epoch), "%" SWITCH_TIME_T_FMT, switch_epoch_time_now(NULL) + delay_next_agent_call);
+ cc_agent_update("ready_time", ready_epoch , h->agent_name);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Agent %s sleeping for %d seconds\n", h->agent_name, delay_next_agent_call);
+ }
+
+ /* Fire up event when contact agent fails */
+ if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", h->queue_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "bridge-agent-fail");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Hangup-Cause", switch_channel_cause2str(cause));
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent", h->agent_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Agent-System", h->agent_system);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", h->member_uuid);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", h->member_caller_name);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", h->member_caller_number);
+ switch_event_fire(&event);
}
}
@@ -1670,14 +1734,14 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
char *sql = NULL;
char res[256];
char *agent_status = argv[2];
- char *agent_tier_state = argv[8];
- char *agent_last_bridge_end = argv[9];
- char *agent_wrap_up_time = argv[10];
- char *agent_state = argv[11];
- char *agent_ready_time = argv[12];
- char *agent_tier_level = argv[13];
- char *agent_type = argv[14];
- char *agent_uuid = argv[15];
+ char *agent_tier_state = argv[9];
+ char *agent_last_bridge_end = argv[10];
+ char *agent_wrap_up_time = argv[11];
+ char *agent_state = argv[12];
+ char *agent_ready_time = argv[13];
+ char *agent_tier_level = argv[14];
+ char *agent_type = argv[15];
+ char *agent_uuid = argv[16];
switch_bool_t contact_agent = SWITCH_TRUE;
@@ -1787,6 +1851,7 @@ static int agents_callback(void *pArg, int argc, char **argv, char **columnNames
h->max_no_answer = atoi(argv[5]);
h->reject_delay_time = atoi(argv[6]);
h->busy_delay_time = atoi(argv[7]);
+ h->no_answer_delay_time = atoi(argv[8]);
cc_agent_update("state", cc_agent_state2str(CC_AGENT_STATE_RECEIVING), h->agent_name);
@@ -1895,13 +1960,13 @@ static int members_callback(void *pArg, int argc, char **argv, char **columnName
switch_safe_free(sql);
sql_order_by = switch_mprintf("level, position");
} else if(!strcasecmp(queue_strategy, "sequentially-by-agent-order")) {
- sql_order_by = switch_mprintf("level, position");
+ sql_order_by = switch_mprintf("level, position, agents.last_offered_call"); /* Default to last_offered_call, let add new strategy if needing it differently */
} else {
/* If the strategy doesn't exist, just fallback to the following */
- sql_order_by = switch_mprintf("level, position");
+ sql_order_by = switch_mprintf("level, position, agents.last_offered_call");
}
- sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time,tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)"
+ sql = switch_mprintf("SELECT system, name, status, contact, no_answer_count, max_no_answer, reject_delay_time, busy_delay_time, no_answer_delay_time, tiers.state, agents.last_bridge_end, agents.wrap_up_time, agents.state, agents.ready_time, tiers.level, agents.type, agents.uuid FROM agents LEFT JOIN tiers ON (agents.name = tiers.agent)"
" WHERE tiers.queue = '%q'"
" AND (agents.status = '%q' OR agents.status = '%q' OR agents.status = '%q')"
" ORDER BY %q",
@@ -2045,7 +2110,7 @@ void *SWITCH_THREAD_FUNC cc_member_thread_run(switch_thread_t *thread, void *obj
switch_channel_set_flag_value(member_channel, CF_BREAK, 2);
}
- /* Will drop the caller if no agent was found for more than X secondes */
+ /* Will drop the caller if no agent was found for more than X seconds */
if (queue->max_wait_time_with_no_agent > 0 && m->t_member_called < queue->last_agent_exist_check - queue->max_wait_time_with_no_agent_time_reached &&
queue->last_agent_exist_check - queue->last_agent_exist >= queue->max_wait_time_with_no_agent) {
m->member_cancel_reason = CC_MEMBER_CANCEL_REASON_NO_AGENT_TIMEOUT;
@@ -2301,52 +2366,63 @@ SWITCH_STANDARD_APP(callcenter_function)
h->running = 0;
}
- /* Hangup any agents been callback */
- if (!switch_channel_up(member_channel)) { /* If channel is still up, it mean that the member didn't hangup, so we should leave the agent alone */
- switch_core_session_hupall_matching_var("cc_member_uuid", member_uuid, SWITCH_CAUSE_ORIGINATOR_CANCEL);
+ /* Check if we were removed be cause FS Core(BREAK) asked us too */
+ if (h->member_cancel_reason == CC_MEMBER_CANCEL_REASON_NONE && !switch_channel_get_variable(member_channel, "cc_agent_uuid")) {
+ h->member_cancel_reason = CC_MEMBER_CANCEL_REASON_BREAK_OUT;
+ }
+
+ /* Canceled for some reason */
+ if (!switch_channel_up(member_channel) || h->member_cancel_reason != CC_MEMBER_CANCEL_REASON_NONE) {
+ /* Update member state */
sql = switch_mprintf("UPDATE members SET state = '%q', uuid = '', abandoned_epoch = '%ld' WHERE system = 'single_box' AND uuid = '%q'",
cc_member_state2str(CC_MEMBER_STATE_ABANDONED), (long) switch_epoch_time_now(NULL), member_uuid);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
- /* Generate an Event and update some channel variable */
+ /* Hangup any callback agents */
+ switch_core_session_hupall_matching_var("cc_member_uuid", member_uuid, SWITCH_CAUSE_ORIGINATOR_CANCEL);
+
+ /* Generate an event */
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(member_channel, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "member-queue-end");
- switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Wait-Time", "%ld", (long) (switch_epoch_time_now(NULL) - t_member_called));
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member \"%s\" <%s> exit queue %s due to %s\n",
- switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")),
- switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")),
- queue_name, cc_member_cancel_reason2str(h->member_cancel_reason));
-
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", cc_member_cancel_reason2str(h->member_cancel_reason));
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Leaving-Time", "%ld", (long) switch_epoch_time_now(NULL));
+ switch_event_add_header(event, SWITCH_STACK_BOTTOM, "CC-Caller-Joined-Time", "%ld", (long) t_member_called);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cause", "Cancel");
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Cancel-Reason", cc_member_cancel_reason2str(h->member_cancel_reason));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-UUID", member_uuid);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Name", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")));
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Caller-CID-Number", switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")));
switch_event_fire(&event);
}
- /* for xml_cdr needs */
+ /* Update some channel variables for xml_cdr needs */
switch_channel_set_variable_printf(member_channel, "cc_queue_canceled_epoch", "%ld", (long) switch_epoch_time_now(NULL));
- switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", cc_member_cancel_reason2str(h->member_cancel_reason));
+ switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", "cancel");
+ switch_channel_set_variable_printf(member_channel, "cc_cancel_reason", "%s", cc_member_cancel_reason2str(h->member_cancel_reason));
-
- /* Send Event with queue count */
- cc_queue_count(queue_name);
+ /* Print some debug log information */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member \"%s\" <%s> exit queue %s due to %s\n",
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_name")),
+ switch_str_nil(switch_channel_get_variable(member_channel, "caller_id_number")),
+ queue_name, cc_member_cancel_reason2str(h->member_cancel_reason));
} else {
- switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", "answered");
+ /* Update member state */
sql = switch_mprintf("UPDATE members SET state = '%q', bridge_epoch = '%ld' WHERE system = 'single_box' AND uuid = '%q'",
cc_member_state2str(CC_MEMBER_STATE_ANSWERED), (long) switch_epoch_time_now(NULL), member_uuid);
cc_execute_sql(NULL, sql, NULL);
switch_safe_free(sql);
- /* Send Event with queue count */
- cc_queue_count(queue_name);
+ /* Update some channel variables for xml_cdr needs */
+ switch_channel_set_variable_printf(member_channel, "cc_cause", "%s", "answered");
}
+ /* Send Event with queue count */
+ cc_queue_count(queue_name);
+
end:
return;
@@ -2394,18 +2470,26 @@ static int list_result_callback(void *pArg, int argc, char **argv, char **column
"callcenter_config agent set ready_time [agent_name] [wait till epoch] | "\
"callcenter_config agent set reject_delay_time [agent_name] [wait second] | "\
"callcenter_config agent set busy_delay_time [agent_name] [wait second] | "\
+"callcenter_config agent set no_answer_delay_time [agent_name] [wait second] | "\
"callcenter_config agent get status [agent_name] | " \
+"callcenter_config agent list | " \
"callcenter_config tier add [queue_name] [agent_name] [level] [position] | " \
"callcenter_config tier set state [queue_name] [agent_name] [state] | " \
"callcenter_config tier set level [queue_name] [agent_name] [level] | " \
"callcenter_config tier set position [queue_name] [agent_name] [position] | " \
"callcenter_config tier del [queue_name] [agent_name] | " \
+"callcenter_config tier list | " \
"callcenter_config queue load [queue_name] | " \
"callcenter_config queue unload [queue_name] | " \
"callcenter_config queue reload [queue_name] | " \
-"callcenter_config tier list [queue_name] | " \
-"callcenter_config queue list [queue_name] | " \
-"callcenter_config queue count [queue_name]"
+"callcenter_config queue list | " \
+"callcenter_config queue list agents [queue_name] [status] | " \
+"callcenter_config queue list members [queue_name] | " \
+"callcenter_config queue list tiers [queue_name] | " \
+"callcenter_config queue count | " \
+"callcenter_config queue count agents [queue_name] [status] | " \
+"callcenter_config queue count members [queue_name] | " \
+"callcenter_config queue count tiers [queue_name]"
SWITCH_STANDARD_API(cc_config_api_function)
{
@@ -2513,6 +2597,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
}
}
+
} else if (action && !strcasecmp(action, "get")) {
if (argc-initial_argc < 2) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
@@ -2538,6 +2623,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
}
}
+
} else if (action && !strcasecmp(action, "list")) {
struct list_result cbt;
cbt.row_process = 0;
@@ -2616,6 +2702,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
goto done;
}
}
+
} else if (action && !strcasecmp(action, "del")) {
if (argc-initial_argc < 2) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
@@ -2635,19 +2722,13 @@ SWITCH_STANDARD_API(cc_config_api_function)
}
} else if (action && !strcasecmp(action, "list")) {
- if (argc-initial_argc < 1) {
- stream->write_function(stream, "%s", "-ERR Invalid!\n");
- goto done;
- } else {
- const char *queue = argv[0 + initial_argc];
- struct list_result cbt;
- cbt.row_process = 0;
- cbt.stream = stream;
- sql = switch_mprintf("SELECT * FROM tiers WHERE queue = '%q' ORDER BY level, position", queue);
- cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, list_result_callback, &cbt /* Call back variables */);
- switch_safe_free(sql);
- stream->write_function(stream, "%s", "+OK\n");
- }
+ struct list_result cbt;
+ cbt.row_process = 0;
+ cbt.stream = stream;
+ sql = switch_mprintf("SELECT * FROM tiers ORDER BY level, position");
+ cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, list_result_callback, &cbt /* Call back variables */);
+ switch_safe_free(sql);
+ stream->write_function(stream, "%s", "+OK\n");
}
} else if (section && !strcasecmp(section, "queue")) {
if (action && !strcasecmp(action, "load")) {
@@ -2664,6 +2745,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%s", "-ERR Invalid Queue not found!\n");
}
}
+
} else if (action && !strcasecmp(action, "unload")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
@@ -2674,6 +2756,7 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%s", "+OK\n");
}
+
} else if (action && !strcasecmp(action, "reload")) {
if (argc-initial_argc < 1) {
stream->write_function(stream, "%s", "-ERR Invalid!\n");
@@ -2689,7 +2772,9 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%s", "-ERR Invalid Queue not found!\n");
}
}
+
} else if (action && !strcasecmp(action, "list")) {
+ /* queue list */
if (argc-initial_argc < 1) {
switch_hash_index_t *hi;
stream->write_function(stream, "%s", "name|strategy|moh_sound|time_base_score|tier_rules_apply|tier_rule_wait_second|tier_rule_wait_multiply_level|tier_rule_no_agent_no_wait|discard_abandoned_after|abandoned_resume_allowed|max_wait_time|max_wait_time_with_no_agent|max_wait_time_with_no_agent_time_reached|record_template\n");
@@ -2708,35 +2793,81 @@ SWITCH_STANDARD_API(cc_config_api_function)
stream->write_function(stream, "%s", "+OK\n");
goto done;
} else {
- const char *queue_name = argv[0 + initial_argc];
+ const char *sub_action = argv[0 + initial_argc];
+ const char *queue_name = argv[1 + initial_argc];
+ const char *status = NULL;
struct list_result cbt;
+
+ /* queue list agents */
+ if (sub_action && !strcasecmp(sub_action, "agents")) {
+ if (argc-initial_argc > 2) {
+ status = argv[2 + initial_argc];
+ }
+ if (status) {
+ sql = switch_mprintf("SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q'", queue_name, status);
+ } else {
+ sql = switch_mprintf("SELECT agents.* FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q'", queue_name);
+ }
+ /* queue list members */
+ } else if (sub_action && !strcasecmp(sub_action, "members")) {
+ sql = switch_mprintf("SELECT * FROM members WHERE queue = '%q';", queue_name);
+ /* queue list tiers */
+ } else if (sub_action && !strcasecmp(sub_action, "tiers")) {
+ sql = switch_mprintf("SELECT * FROM tiers WHERE queue = '%q';", queue_name);
+ } else {
+ stream->write_function(stream, "%s", "-ERR Invalid!\n");
+ goto done;
+ }
+
cbt.row_process = 0;
cbt.stream = stream;
- sql = switch_mprintf("SELECT * FROM members WHERE queue = '%q'", queue_name);
cc_execute_sql_callback(NULL /* queue */, NULL /* mutex */, sql, list_result_callback, &cbt /* Call back variables */);
switch_safe_free(sql);
stream->write_function(stream, "%s", "+OK\n");
}
+
} else if (action && !strcasecmp(action, "count")) {
+ /* queue count */
if (argc-initial_argc < 1) {
- stream->write_function(stream, "%s", "-ERR Invalid!\n");
+ switch_hash_index_t *hi;
+ int queue_count = 0;
+ switch_mutex_lock(globals.mutex);
+ for (hi = switch_hash_first(NULL, globals.queue_hash); hi; hi = switch_hash_next(hi)) {
+ queue_count++;
+ }
+ switch_mutex_unlock(globals.mutex);
+ stream->write_function(stream, "%d\n", queue_count);
goto done;
} else {
- const char *queue_name = argv[0 + initial_argc];
+ const char *sub_action = argv[0 + initial_argc];
+ const char *queue_name = argv[1 + initial_argc];
+ const char *status = NULL;
char res[256] = "";
- switch_event_t *event;
- /* Check to see if agent already exist */
- sql = switch_mprintf("SELECT count(*) FROM members WHERE queue = '%q'", queue_name);
+
+ /* queue count agents */
+ if (sub_action && !strcasecmp(sub_action, "agents")) {
+ if (argc-initial_argc > 2) {
+ status = argv[2 + initial_argc];
+ }
+ if (status) {
+ sql = switch_mprintf("SELECT count(*) FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q' AND agents.status = '%q'", queue_name, status);
+ } else {
+ sql = switch_mprintf("SELECT count(*) FROM agents,tiers WHERE tiers.agent = agents.name AND tiers.queue = '%q'", queue_name);
+ }
+ /* queue count members */
+ } else if (sub_action && !strcasecmp(sub_action, "members")) {
+ sql = switch_mprintf("SELECT count(*) FROM members WHERE queue = '%q';", queue_name);
+ /* queue count tiers */
+ } else if (sub_action && !strcasecmp(sub_action, "tiers")) {
+ sql = switch_mprintf("SELECT count(*) FROM tiers WHERE queue = '%q';", queue_name);
+ } else {
+ stream->write_function(stream, "%s", "-ERR Invalid!\n");
+ goto done;
+ }
+
cc_execute_sql2str(NULL, NULL, sql, res, sizeof(res));
switch_safe_free(sql);
stream->write_function(stream, "%d\n", atoi(res));
-
- if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CALLCENTER_EVENT) == SWITCH_STATUS_SUCCESS) {
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Queue", queue_name);
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Action", "members-count");
- switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CC-Count", res);
- switch_event_fire(&event);
- }
}
}
}
@@ -2789,10 +2920,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_callcenter_load)
switch_console_set_complete("add callcenter_config agent set ready_time");
switch_console_set_complete("add callcenter_config agent set reject_delay_time");
switch_console_set_complete("add callcenter_config agent set busy_delay_time");
+ switch_console_set_complete("add callcenter_config agent set no_answer_delay_time");
switch_console_set_complete("add callcenter_config agent get status");
switch_console_set_complete("add callcenter_config agent list");
-
switch_console_set_complete("add callcenter_config tier add");
switch_console_set_complete("add callcenter_config tier del");
switch_console_set_complete("add callcenter_config tier set state");
@@ -2804,7 +2935,13 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_callcenter_load)
switch_console_set_complete("add callcenter_config queue unload");
switch_console_set_complete("add callcenter_config queue reload");
switch_console_set_complete("add callcenter_config queue list");
+ switch_console_set_complete("add callcenter_config queue list agents");
+ switch_console_set_complete("add callcenter_config queue list members");
+ switch_console_set_complete("add callcenter_config queue list tiers");
switch_console_set_complete("add callcenter_config queue count");
+ switch_console_set_complete("add callcenter_config queue count agents");
+ switch_console_set_complete("add callcenter_config queue count members");
+ switch_console_set_complete("add callcenter_config queue count tiers");
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c
index d0f5fac081..0f5ea26e09 100644
--- a/src/mod/applications/mod_conference/mod_conference.c
+++ b/src/mod/applications/mod_conference/mod_conference.c
@@ -37,6 +37,7 @@
*/
#include
#define DEFAULT_AGC_LEVEL 1100
+#define CONFERENCE_UUID_VARIABLE "conference_uuid"
SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown);
@@ -564,7 +565,10 @@ static conference_member_t *conference_member_get(conference_obj_t *conference,
member = NULL;
}
- switch_thread_rwlock_rdlock(member->rwlock);
+ if (member) {
+ switch_thread_rwlock_rdlock(member->rwlock);
+ }
+
switch_mutex_unlock(conference->member_mutex);
return member;
@@ -702,6 +706,7 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
channel = switch_core_session_get_channel(member->session);
switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id);
+ switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str);
if (conference->count > 1) {
if (conference->moh_sound && !switch_test_flag(conference, CFLAG_WAIT_MOD)) {
@@ -4625,6 +4630,7 @@ static switch_status_t conf_api_sub_transfer(conference_obj_t *conference, switc
/* move the member from the old conference to the new one */
lock_member(member);
+ switch_thread_rwlock_unlock(member->rwlock);
if (conference != new_conference) {
conference_del_member(conference, member);
@@ -4660,10 +4666,6 @@ static switch_status_t conf_api_sub_transfer(conference_obj_t *conference, switc
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "transfer");
switch_event_fire(&event);
}
-
- if (member) {
- switch_thread_rwlock_unlock(member->rwlock);
- }
}
if (new_conference) {
diff --git a/src/mod/applications/mod_ladspa/Makefile b/src/mod/applications/mod_ladspa/Makefile
new file mode 100644
index 0000000000..1a77c52a0d
--- /dev/null
+++ b/src/mod/applications/mod_ladspa/Makefile
@@ -0,0 +1,5 @@
+BASE=../../../..
+
+LOCAL_OBJS += load.o
+include $(BASE)/build/modmake.rules
+
diff --git a/src/mod/applications/mod_ladspa/load.c b/src/mod/applications/mod_ladspa/load.c
new file mode 100644
index 0000000000..652ecfa0f2
--- /dev/null
+++ b/src/mod/applications/mod_ladspa/load.c
@@ -0,0 +1,173 @@
+/* load.c
+
+ Free software by Richard W.E. Furse. Do with as you will. No
+ warranty. */
+
+/*****************************************************************************/
+
+#include
+#include
+#include
+#include
+
+/*****************************************************************************/
+
+#include "ladspa.h"
+#include "utils.h"
+#include "inttypes.h"
+#include "switch.h"
+/*****************************************************************************/
+
+/* This function provides a wrapping of dlopen(). When the filename is
+ not an absolute path (i.e. does not begin with / character), this
+ routine will search the LADSPA_PATH for the file. */
+static void *dlopenLADSPA(const char *pcFilename, int iFlag)
+{
+
+ char *pcBuffer;
+ const char *pcEnd;
+ const char *pcLADSPAPath;
+ const char *pcStart;
+ int iEndsInSO;
+ int iNeedSlash;
+ size_t iFilenameLength;
+ void *pvResult;
+
+ iFilenameLength = strlen(pcFilename);
+ pvResult = NULL;
+
+ if (pcFilename[0] == '/') {
+
+ /* The filename is absolute. Assume the user knows what he/she is
+ doing and simply dlopen() it. */
+
+ pvResult = dlopen(pcFilename, iFlag);
+ if (pvResult != NULL)
+ return pvResult;
+
+ } else {
+
+ /* If the filename is not absolute then we wish to check along the
+ LADSPA_PATH path to see if we can find the file there. We do
+ NOT call dlopen() directly as this would find plugins on the
+ LD_LIBRARY_PATH, whereas the LADSPA_PATH is the correct place
+ to search. */
+
+ pcLADSPAPath = getenv("LADSPA_PATH");
+
+ if (pcLADSPAPath) {
+
+ pcStart = pcLADSPAPath;
+ while (*pcStart != '\0') {
+ pcEnd = pcStart;
+ while (*pcEnd != ':' && *pcEnd != '\0')
+ pcEnd++;
+
+ pcBuffer = malloc(iFilenameLength + 2 + (pcEnd - pcStart));
+ if (pcEnd > pcStart)
+ strncpy(pcBuffer, pcStart, pcEnd - pcStart);
+ iNeedSlash = 0;
+ if (pcEnd > pcStart)
+ if (*(pcEnd - 1) != '/') {
+ iNeedSlash = 1;
+ pcBuffer[pcEnd - pcStart] = '/';
+ }
+ strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename);
+
+ pvResult = dlopen(pcBuffer, iFlag);
+
+ free(pcBuffer);
+ if (pvResult != NULL)
+ return pvResult;
+
+ pcStart = pcEnd;
+ if (*pcStart == ':')
+ pcStart++;
+ }
+ }
+ }
+
+ /* As a last ditch effort, check if filename does not end with
+ ".so". In this case, add this suffix and recurse. */
+ iEndsInSO = 0;
+ if (iFilenameLength > 3)
+ iEndsInSO = (strcmp(pcFilename + iFilenameLength - 3, ".so") == 0);
+ if (!iEndsInSO) {
+ pcBuffer = malloc(iFilenameLength + 4);
+ strcpy(pcBuffer, pcFilename);
+ strcat(pcBuffer, ".so");
+ pvResult = dlopenLADSPA(pcBuffer, iFlag);
+ free(pcBuffer);
+ }
+
+ if (pvResult != NULL)
+ return pvResult;
+
+ /* If nothing has worked, then at least we can make sure we set the
+ correct error message - and this should correspond to a call to
+ dlopen() with the actual filename requested. The dlopen() manual
+ page does not specify whether the first or last error message
+ will be kept when multiple calls are made to dlopen(). We've
+ covered the former case - now we can handle the latter by calling
+ dlopen() again here. */
+ return dlopen(pcFilename, iFlag);
+}
+
+/*****************************************************************************/
+
+void *loadLADSPAPluginLibrary(const char *pcPluginFilename)
+{
+
+ void *pvPluginHandle;
+
+ pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW);
+ if (!pvPluginHandle) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load plugin \"%s\": %s\n", pcPluginFilename, dlerror());
+ }
+
+ return pvPluginHandle;
+}
+
+/*****************************************************************************/
+
+void unloadLADSPAPluginLibrary(void *pvLADSPAPluginLibrary)
+{
+ dlclose(pvLADSPAPluginLibrary);
+}
+
+/*****************************************************************************/
+
+const LADSPA_Descriptor *findLADSPAPluginDescriptor(void *pvLADSPAPluginLibrary, const char *pcPluginLibraryFilename, const char *pcPluginLabel)
+{
+
+ const LADSPA_Descriptor *psDescriptor;
+ LADSPA_Descriptor_Function pfDescriptorFunction;
+ unsigned long lPluginIndex;
+
+ dlerror();
+ pfDescriptorFunction = (LADSPA_Descriptor_Function) (intptr_t)dlsym(pvLADSPAPluginLibrary, "ladspa_descriptor");
+ if (!pfDescriptorFunction) {
+ const char *pcError = dlerror();
+ if (pcError) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+ "Unable to find ladspa_descriptor() function in plugin "
+ "library file \"%s\": %s.\n" "Are you sure this is a LADSPA plugin file?\n", pcPluginLibraryFilename, pcError);
+ return NULL;
+ }
+ }
+
+ for (lPluginIndex = 0;; lPluginIndex++) {
+ psDescriptor = pfDescriptorFunction(lPluginIndex);
+ if (psDescriptor == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+ "Unable to find label \"%s\" in plugin library file \"%s\".\n", pcPluginLabel, pcPluginLibraryFilename);
+ return NULL;
+ }
+ if (strcmp(psDescriptor->Label, pcPluginLabel) == 0)
+ return psDescriptor;
+ }
+}
+
+/*****************************************************************************/
+
+/* EOF */
diff --git a/src/mod/applications/mod_ladspa/mod_ladspa.c b/src/mod/applications/mod_ladspa/mod_ladspa.c
new file mode 100644
index 0000000000..43ea87a3ca
--- /dev/null
+++ b/src/mod/applications/mod_ladspa/mod_ladspa.c
@@ -0,0 +1,674 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2011, Anthony Minessale II
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II
+ *
+ * mod_ladspa.c -- LADSPA
+ *
+ */
+#include
+#include "ladspa.h"
+#include "utils.h"
+
+/* Prototypes */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_ladspa_shutdown);
+SWITCH_MODULE_RUNTIME_FUNCTION(mod_ladspa_runtime);
+SWITCH_MODULE_LOAD_FUNCTION(mod_ladspa_load);
+
+/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
+ * Defines a switch_loadable_module_function_table_t and a static const char[] modname
+ */
+SWITCH_MODULE_DEFINITION(mod_ladspa, mod_ladspa_load, mod_ladspa_shutdown, NULL);
+
+#define MAX_INDEX 256
+
+typedef struct {
+ switch_core_session_t *session;
+ char *plugin_name;
+ char *label_name;
+ void *library_handle;
+ const LADSPA_Descriptor *ldesc;
+ LADSPA_Handle handle;
+ LADSPA_Data config[MAX_INDEX];
+ int num_idx;
+ char *str_config[MAX_INDEX];
+ int str_idx;
+ uint8_t has_config[MAX_INDEX];
+ int skip;
+ LADSPA_Data in_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ LADSPA_Data file_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ LADSPA_Data out_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+ LADSPA_Data out_ports[MAX_INDEX];
+ switch_file_handle_t fh;
+} switch_ladspa_t;
+
+
+
+int check_range(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data val)
+{
+ if (ldesc->PortRangeHints[i].LowerBound && ldesc->PortRangeHints[i].UpperBound &&
+ (val < ldesc->PortRangeHints[i].LowerBound || val > ldesc->PortRangeHints[i].UpperBound)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Param %f out of bounds %f-%f\n",
+ val, ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound);
+ return 0;
+ }
+
+ return 1;
+}
+
+int find_default(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data *ptr)
+
+{
+ LADSPA_Data dftval = 0;
+ int fail = 0;
+
+ LADSPA_PortRangeHintDescriptor port_hint = ldesc->PortRangeHints[i].HintDescriptor;
+
+ switch (port_hint & LADSPA_HINT_DEFAULT_MASK) {
+ case LADSPA_HINT_DEFAULT_NONE:
+ break;
+ case LADSPA_HINT_DEFAULT_MINIMUM:
+ dftval = ldesc->PortRangeHints[i].LowerBound;
+ break;
+ case LADSPA_HINT_DEFAULT_LOW:
+ if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
+ dftval = exp(log(ldesc->PortRangeHints[i].LowerBound)
+ * 0.75 + log(ldesc->PortRangeHints[i].UpperBound)
+ * 0.25);
+ } else {
+ dftval = (ldesc->PortRangeHints[i].LowerBound * 0.75 + ldesc->PortRangeHints[i].UpperBound * 0.25);
+ }
+ break;
+ case LADSPA_HINT_DEFAULT_MIDDLE:
+ if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
+ dftval = sqrt(ldesc->PortRangeHints[i].LowerBound * ldesc->PortRangeHints[i].UpperBound);
+ } else {
+ dftval = 0.5 * (ldesc->PortRangeHints[i].LowerBound + ldesc->PortRangeHints[i].UpperBound);
+ }
+ break;
+ case LADSPA_HINT_DEFAULT_HIGH:
+ if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
+ dftval = exp(log(ldesc->PortRangeHints[i].LowerBound)
+ * 0.25 + log(ldesc->PortRangeHints[i].UpperBound)
+ * 0.75);
+ } else {
+ dftval = (ldesc->PortRangeHints[i].LowerBound * 0.25 + ldesc->PortRangeHints[i].UpperBound * 0.75);
+ }
+ break;
+ case LADSPA_HINT_DEFAULT_MAXIMUM:
+ dftval = ldesc->PortRangeHints[i].UpperBound;
+ break;
+ case LADSPA_HINT_DEFAULT_0:
+ dftval = 0;
+ break;
+ case LADSPA_HINT_DEFAULT_1:
+ dftval = 1;
+ break;
+ case LADSPA_HINT_DEFAULT_100:
+ dftval = 100;
+ break;
+ case LADSPA_HINT_DEFAULT_440:
+ dftval = 440;
+ break;
+ default:
+ fail = 1;
+ break;
+ }
+
+ if (!fail) {
+ *ptr = dftval;
+ }
+
+ return !fail;
+}
+
+static void dump_info(const LADSPA_Descriptor *ldesc)
+{
+ int i = 0;
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Name: \"%s\"\n", ldesc->Name);
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Label: \"%s\"\n", ldesc->Label);
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Unique ID: %lu\n", ldesc->UniqueID);
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Maker: \"%s\"\n", ldesc->Maker);
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Copyright: \"%s\"\n", ldesc->Copyright);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Must Run Real-Time: ");
+ if (LADSPA_IS_REALTIME(ldesc->Properties))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has activate() Function: ");
+ if (ldesc->activate != NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has deactivate() Function: ");
+ if (ldesc->deactivate != NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has run_adding() Function: ");
+ if (ldesc->run_adding != NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
+
+ if (ldesc->instantiate == NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO INSTANTIATE FUNCTION.\n");
+ if (ldesc->connect_port == NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CONNECT_PORT FUNCTION.\n");
+ if (ldesc->run == NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO RUN FUNCTION.\n");
+ if (ldesc->run_adding != NULL && ldesc->set_run_adding_gain == NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS RUN_ADDING FUNCTION BUT " "NOT SET_RUN_ADDING_GAIN.\n");
+ if (ldesc->run_adding == NULL && ldesc->set_run_adding_gain != NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS SET_RUN_ADDING_GAIN FUNCTION BUT " "NOT RUN_ADDING.\n");
+ if (ldesc->cleanup == NULL)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CLEANUP FUNCTION.\n");
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Environment: ");
+ if (LADSPA_IS_HARD_RT_CAPABLE(ldesc->Properties))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal or Hard Real-Time\n");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal\n");
+
+ if (LADSPA_IS_INPLACE_BROKEN(ldesc->Properties))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "This plugin cannot use in-place processing. " "It will not work with all hosts.\n");
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Ports:");
+
+ if (ldesc->PortCount == 0)
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\tERROR: PLUGIN HAS NO PORTS.\n");
+
+ for (i = 0; i < ldesc->PortCount; i++) {
+ LADSPA_Data dft = 0.0f;
+ int found = 0;
+
+ if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) {
+ found = find_default(ldesc, i, &dft);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n \"%s\" ", ldesc->PortNames[i]);
+
+ if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i])
+ && LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: INPUT AND OUTPUT");
+ else if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "input");
+ else if (LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "output");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: NEITHER INPUT NOR OUTPUT");
+
+ if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])
+ && LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: CONTROL AND AUDIO");
+ else if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", control");
+ else if (LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i]))
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", audio");
+ else
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: NEITHER CONTROL NOR AUDIO");
+
+ if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) {
+ if (found) {
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: %f\n",
+ ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound, dft);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: none.\n",
+ ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound);
+ }
+ }
+
+
+
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n\n");
+}
+
+
+
+
+
+static switch_bool_t ladspa_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+ switch_ladspa_t *pvt = (switch_ladspa_t *) user_data;
+ //switch_frame_t *frame = NULL;
+ switch_channel_t *channel = switch_core_session_get_channel(pvt->session);
+
+ switch (type) {
+ case SWITCH_ABC_TYPE_INIT:
+ {
+ switch_codec_implementation_t read_impl = { 0 };
+ LADSPA_PortDescriptor port_desc;
+ int i = 0, j = 0, k = 0, str_idx = 0;
+
+ switch_core_session_get_read_impl(pvt->session, &read_impl);
+
+ if (!(pvt->library_handle = loadLADSPAPluginLibrary(pvt->plugin_name))) {
+ return SWITCH_FALSE;
+ }
+
+ if (!(pvt->ldesc = findLADSPAPluginDescriptor(pvt->library_handle, pvt->plugin_name, pvt->label_name))) {
+ return SWITCH_FALSE;
+ }
+
+
+ pvt->handle = pvt->ldesc->instantiate(pvt->ldesc, read_impl.actual_samples_per_second);
+
+ dump_info(pvt->ldesc);
+
+
+ for (i = 0; i < pvt->ldesc->PortCount; i++) {
+ port_desc = pvt->ldesc->PortDescriptors[i];
+
+ if (LADSPA_IS_PORT_CONTROL(port_desc) && LADSPA_IS_PORT_INPUT(port_desc)) {
+ LADSPA_Data dft = 0.0f;
+ int found = find_default(pvt->ldesc, i, &dft);
+
+ if (found && !pvt->has_config[j]) {
+ pvt->config[j] = dft;
+ pvt->has_config[j] = 1;
+ }
+
+ if (pvt->has_config[j]) {
+ if (!check_range(pvt->ldesc, i, pvt->config[j])) {
+ pvt->config[j] = dft;
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_WARNING, "FALLING TO DEFAULT PARAM %d [%s] (%f)\n",
+ j+1,
+ pvt->ldesc->PortNames[i],
+ pvt->config[j]);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "ADDING PARAM %d [%s] (%f)\n",
+ j+1,
+ pvt->ldesc->PortNames[i],
+ pvt->config[j]);
+ pvt->ldesc->connect_port(pvt->handle, i, &pvt->config[j++]);
+ usleep(10000);
+ }
+ }
+
+ if (LADSPA_IS_PORT_INPUT(port_desc) && LADSPA_IS_PORT_AUDIO(port_desc)) {
+ int mapped = 0;
+
+ if (pvt->str_idx && !zstr(pvt->str_config[str_idx])) {
+
+ if (!strcasecmp(pvt->str_config[str_idx], "none")) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT NOTHING to port: %s\n",
+ pvt->ldesc->PortNames[i]
+ );
+ mapped = 1;
+ } else if (!strncasecmp(pvt->str_config[str_idx], "file:", 5)) {
+ char *file = pvt->str_config[str_idx] + 5;
+
+ if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session),
+ SWITCH_LOG_ERROR, "CAN'T CONNECT FILE [%s] File already mapped\n", file);
+ } else {
+ if (switch_core_file_open(&pvt->fh,
+ file,
+ read_impl.number_of_channels,
+ read_impl.actual_samples_per_second,
+ SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot open file: %s\n", file);
+ return SWITCH_FALSE;
+ }
+
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT FILE [%s] to port: %s\n",
+ file,
+ pvt->ldesc->PortNames[i]
+ );
+
+ pvt->ldesc->connect_port(pvt->handle, i, pvt->file_buf);
+ mapped = 1;
+ }
+ }
+
+ str_idx++;
+ }
+
+ if (!mapped) {
+ pvt->ldesc->connect_port(pvt->handle, i, pvt->in_buf);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT CHANNEL AUDIO to port: %s\n",
+ pvt->ldesc->PortNames[i]
+ );
+ }
+
+ }
+
+ if (LADSPA_IS_PORT_OUTPUT(port_desc)) {
+ if (LADSPA_IS_PORT_AUDIO(port_desc)) {
+ pvt->ldesc->connect_port(pvt->handle, i, pvt->out_buf);
+ } else if (k < MAX_INDEX) {
+ pvt->ldesc->connect_port(pvt->handle, i, &pvt->out_ports[k++]);
+ }
+ }
+ }
+ }
+
+ break;
+
+ case SWITCH_ABC_TYPE_CLOSE:
+ {
+
+ if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
+ switch_core_file_close(&pvt->fh);
+ }
+
+ if (pvt->handle && pvt->ldesc) {
+ pvt->ldesc->cleanup(pvt->handle);
+ }
+
+ if (pvt->library_handle) {
+ unloadLADSPAPluginLibrary(pvt->library_handle);
+ }
+ }
+ break;
+
+ case SWITCH_ABC_TYPE_WRITE_REPLACE:
+ case SWITCH_ABC_TYPE_READ_REPLACE:
+ {
+ switch_frame_t *rframe;
+ int16_t *slin, abuf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
+ switch_size_t olen = 0;
+
+
+ if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
+ rframe = switch_core_media_bug_get_read_replace_frame(bug);
+ } else {
+ rframe = switch_core_media_bug_get_write_replace_frame(bug);
+ }
+
+ slin = rframe->data;
+
+ if (switch_channel_media_ready(channel)) {
+ switch_short_to_float(slin, pvt->in_buf, rframe->samples);
+
+ if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
+ olen = rframe->samples;
+ if (switch_core_file_read(&pvt->fh, abuf, &olen) != SWITCH_STATUS_SUCCESS) {
+ switch_codec_implementation_t read_impl = { 0 };
+ char *file = switch_core_session_strdup(pvt->session, pvt->fh.file_path);
+ switch_core_session_get_read_impl(pvt->session, &read_impl);
+
+ switch_core_file_close(&pvt->fh);
+
+ if (switch_core_file_open(&pvt->fh,
+ file,
+ read_impl.number_of_channels,
+ read_impl.actual_samples_per_second,
+ SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot open file: %s\n", file);
+ return SWITCH_FALSE;
+ }
+
+ olen = rframe->samples;
+ if (switch_core_file_read(&pvt->fh, abuf, &olen) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot READ file: %s\n", file);
+ return SWITCH_FALSE;
+ }
+ }
+
+ switch_short_to_float(abuf, pvt->file_buf, olen);
+ }
+
+ pvt->ldesc->run(pvt->handle, rframe->samples);
+
+ switch_float_to_short(pvt->out_buf, slin, rframe->samples);
+ }
+
+ if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
+ switch_core_media_bug_set_read_replace_frame(bug, rframe);
+ } else {
+ switch_core_media_bug_set_write_replace_frame(bug, rframe);
+ }
+
+ if (pvt->skip && !--pvt->skip) {
+ return SWITCH_FALSE;
+ }
+
+ }
+ break;
+ case SWITCH_ABC_TYPE_WRITE:
+ default:
+ break;
+ }
+
+ return SWITCH_TRUE;
+}
+
+switch_status_t stop_ladspa_session(switch_core_session_t *session)
+{
+ switch_media_bug_t *bug;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+ if ((bug = switch_channel_get_private(channel, "ladspa"))) {
+ switch_channel_set_private(channel, "ladspa", NULL);
+ switch_core_media_bug_remove(session, &bug);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
+switch_status_t ladspa_session(switch_core_session_t *session, const char *flags, const char *plugin_name, const char *label, const char *params)
+{
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+ switch_media_bug_t *bug;
+ switch_status_t status;
+ switch_ladspa_t *pvt = { 0 };
+ switch_codec_implementation_t read_impl = { 0 };
+ int i, bflags = SMBF_READ_REPLACE | SMBF_ANSWER_REQ;
+ char *pstr;
+ int argc;
+ char *argv[50];
+ char *dparams = NULL;
+
+ if (zstr(plugin_name)) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s INVALID PLUGIN\n", switch_channel_get_name(channel));
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (zstr(flags)) {
+ flags = "r";
+ }
+
+ if (strchr(flags, 'w')) {
+ bflags = SMBF_WRITE_REPLACE;
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "FLAGS: %s PLUGIN: %s LABEL: %s PARAMS: %s\n",
+ flags, plugin_name, label, params);
+
+ switch_core_session_get_read_impl(session, &read_impl);
+
+ pvt = switch_core_session_alloc(session, sizeof(*pvt));
+
+ pvt->session = session;
+ if (!zstr(label)) {
+ pvt->label_name = switch_core_session_strdup(session, label);
+ } else {
+ char *p;
+ pvt->label_name = switch_core_session_strdup(session, plugin_name);
+ if ((p = strrchr(pvt->label_name, '.'))) {
+ *p = '\0';
+ }
+ }
+
+ if (strstr(plugin_name, ".so")) {
+ pvt->plugin_name = switch_core_session_strdup(session, plugin_name);
+ } else {
+ pvt->plugin_name = switch_core_session_sprintf(session, "%s.so", plugin_name);
+ }
+
+ dparams = switch_core_session_strdup(session, params);
+
+ argc = switch_split(dparams, ' ', argv);
+
+ for (i = 0; i < argc; i++) {
+ if (switch_is_number(argv[i])) {
+ if (pvt->num_idx < MAX_INDEX) {
+ pvt->config[pvt->num_idx] = atof(argv[i]);
+ pvt->has_config[pvt->num_idx] = 1;
+ pvt->num_idx++;
+ }
+ } else {
+ if (pvt->str_idx < MAX_INDEX) {
+ pvt->str_config[pvt->str_idx++] = switch_core_session_strdup(session, argv[i]);
+ }
+ }
+ }
+
+ if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ pstr = switch_core_session_sprintf(session, "%s|%s|%s|%s", flags, plugin_name, label, params);
+
+ if ((status = switch_core_media_bug_add(session, "ladspa", pstr,
+ ladspa_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }
+
+ switch_channel_set_private(channel, "ladspa", bug);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+static void ladspa_parse(switch_core_session_t *session, const char *data)
+{
+ char *argv[5] = { 0 };
+ int argc;
+ char *lbuf;
+
+ if (data) {
+ lbuf = strdup(data);
+ argc = switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0])));
+ ladspa_session(session, argv[0], argv[1], argv[2], argv[3]);
+ free(lbuf);
+ }
+}
+
+#define APP_SYNTAX "||