diff --git a/conf/testing/autoload_configs/sndfile.conf.xml b/conf/testing/autoload_configs/sndfile.conf.xml
new file mode 100644
index 0000000000..d9e0cff75d
--- /dev/null
+++ b/conf/testing/autoload_configs/sndfile.conf.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/conf/vanilla/autoload_configs/sndfile.conf.xml b/conf/vanilla/autoload_configs/sndfile.conf.xml
new file mode 100644
index 0000000000..d9e0cff75d
--- /dev/null
+++ b/conf/vanilla/autoload_configs/sndfile.conf.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/src/mod/formats/mod_sndfile/Makefile.am b/src/mod/formats/mod_sndfile/Makefile.am
index e94b9cb100..27f77440e3 100644
--- a/src/mod/formats/mod_sndfile/Makefile.am
+++ b/src/mod/formats/mod_sndfile/Makefile.am
@@ -3,12 +3,30 @@ MODNAME=mod_sndfile
if HAVE_SNDFILE
+noinst_LTLIBRARIES = libsndfilemod.la
+libsndfilemod_la_SOURCES = mod_sndfile.c
+libsndfilemod_la_CFLAGS = $(AM_CFLAGS) $(SNDFILE_CFLAGS)
+
mod_LTLIBRARIES = mod_sndfile.la
-mod_sndfile_la_SOURCES = mod_sndfile.c
+mod_sndfile_la_SOURCES =
mod_sndfile_la_CFLAGS = $(AM_CFLAGS) $(SNDFILE_CFLAGS)
-mod_sndfile_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SNDFILE_LIBS)
+mod_sndfile_la_LIBADD = libsndfilemod.la $(switch_builddir)/libfreeswitch.la $(SNDFILE_LIBS)
mod_sndfile_la_LDFLAGS = -avoid-version -module -no-undefined -shared
+noinst_PROGRAMS = test/test_sndfile test/test_sndfile_conf
+
+test_test_sndfile_SOURCES = test/test_sndfile.c
+test_test_sndfile_CFLAGS = $(AM_CFLAGS) -I./ -I../ -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
+test_test_sndfile_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
+test_test_sndfile_LDADD = libsndfilemod.la
+
+test_test_sndfile_conf_SOURCES = test/test_sndfile_conf.c
+test_test_sndfile_conf_CFLAGS = $(AM_CFLAGS) -I./ -I../ -DSWITCH_TEST_BASE_DIR_FOR_CONF=\"${abs_builddir}/test\" -DSWITCH_TEST_BASE_DIR_OVERRIDE=\"${abs_builddir}/test\"
+test_test_sndfile_conf_LDFLAGS = $(AM_LDFLAGS) -avoid-version -no-undefined $(freeswitch_LDFLAGS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS)
+test_test_sndfile_conf_LDADD = libsndfilemod.la
+
+TESTS = $(noinst_PROGRAMS)
+
else
install: error
all: error
diff --git a/src/mod/formats/mod_sndfile/mod_sndfile.c b/src/mod/formats/mod_sndfile/mod_sndfile.c
index a03e28af5c..7e826b7c46 100644
--- a/src/mod/formats/mod_sndfile/mod_sndfile.c
+++ b/src/mod/formats/mod_sndfile/mod_sndfile.c
@@ -39,6 +39,9 @@ SWITCH_MODULE_DEFINITION(mod_sndfile, mod_sndfile_load, mod_sndfile_shutdown, NU
static struct {
switch_hash_t *format_hash;
+ int debug;
+ char *allowed_extensions[100];
+ int allowed_extensions_count;
} globals;
struct format_map {
@@ -56,6 +59,16 @@ typedef struct sndfile_context sndfile_context;
static switch_status_t sndfile_perform_open(sndfile_context *context, const char *path, int mode, switch_file_handle_t *handle);
+static void reverse_channel_count(switch_file_handle_t *handle) {
+ /* for recording stereo conferences and stereo calls in audio file formats that support only 1 channel.
+ * "{force_channels=1}" does similar, but here switch_core_open_file() was already called and we
+ * have the handle and we chane the count before _read_ or _write_ are called (where muxing is done). */
+ if (handle->channels > 1) {
+ handle->real_channels = handle->channels;
+ handle->channels = handle->mm.channels = 1;
+ }
+}
+
static switch_status_t sndfile_file_open(switch_file_handle_t *handle, const char *path)
{
sndfile_context *context;
@@ -119,41 +132,107 @@ static switch_status_t sndfile_file_open(switch_file_handle_t *handle, const cha
if (!strcmp(ext, "raw")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 8000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "r8")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
- context->sfinfo.samplerate = 8000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 8000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "r16")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
- context->sfinfo.samplerate = 16000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 16000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "r24")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_24;
- context->sfinfo.samplerate = 24000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 24000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "r32")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32;
- context->sfinfo.samplerate = 32000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 32000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "gsm")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_GSM610;
context->sfinfo.channels = 1;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
context->sfinfo.samplerate = 8000;
} else if (!strcmp(ext, "ul") || !strcmp(ext, "ulaw")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ULAW;
- context->sfinfo.channels = 1;
- context->sfinfo.samplerate = 8000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 8000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "al") || !strcmp(ext, "alaw")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ALAW;
- context->sfinfo.channels = 1;
- context->sfinfo.samplerate = 8000;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = 8000;
+ context->sfinfo.channels = 1;
+ }
} else if (!strcmp(ext, "vox")) {
context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_VOX_ADPCM;
context->sfinfo.channels = 1;
context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
} else if (!strcmp(ext, "adpcm")) {
context->sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_IMA_ADPCM;
context->sfinfo.channels = 1;
context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
} else if (!strcmp(ext, "oga") || !strcmp(ext, "ogg")) {
context->sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
- context->sfinfo.samplerate = handle->samplerate;
+ if (mode & SFM_READ) {
+ context->sfinfo.samplerate = handle->samplerate;
+ }
+ } else if (!strcmp(ext, "wve")) {
+ context->sfinfo.format = SF_FORMAT_WVE | SF_FORMAT_ALAW;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
+ } else if (!strcmp(ext, "htk")) {
+ context->sfinfo.format = SF_FORMAT_HTK | SF_FORMAT_PCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
+ } else if (!strcmp(ext, "iff")) {
+ context->sfinfo.format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
+ } else if (!strcmp(ext, "xi")) {
+ context->sfinfo.format = SF_FORMAT_XI | SF_FORMAT_DPCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 44100;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
+ } else if (!strcmp(ext, "sds")) {
+ context->sfinfo.format = SF_FORMAT_SDS | SF_FORMAT_PCM_16;
+ context->sfinfo.channels = 1;
+ context->sfinfo.samplerate = 8000;
+ if (mode & SFM_WRITE) {
+ reverse_channel_count(handle);
+ }
}
if ((mode & SFM_WRITE) && sf_format_check(&context->sfinfo) == 0) {
@@ -204,7 +283,10 @@ static switch_status_t sndfile_file_open(switch_file_handle_t *handle, const cha
goto end;
}
}
- //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opening File [%s] rate %dhz\n", path, context->sfinfo.samplerate);
+ if (globals.debug) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
+ "Opening File [%s] rate [%dhz] channels: [%d]\n", path, context->sfinfo.samplerate, (uint8_t) context->sfinfo.channels);
+ }
handle->samples = (unsigned int) context->sfinfo.frames;
handle->samplerate = context->sfinfo.samplerate;
handle->channels = (uint8_t) context->sfinfo.channels;
@@ -365,6 +447,20 @@ static switch_status_t sndfile_file_get_string(switch_file_handle_t *handle, swi
return SWITCH_STATUS_FALSE;
}
+static switch_bool_t exten_is_allowed(const char *exten) {
+ int i;
+ if (!globals.allowed_extensions[0]) {
+ // defaults to allowing all extensions if param "allowed-extensions" not set in cfg
+ return SWITCH_TRUE;
+ }
+ for (i = 0 ; i < globals.allowed_extensions_count; i++) {
+ if (exten && globals.allowed_extensions[i] && !strcasecmp(globals.allowed_extensions[i], exten)) {
+ return SWITCH_TRUE;
+ }
+ }
+ return SWITCH_FALSE;
+}
+
/* Registration */
static char **supported_formats;
@@ -407,6 +503,9 @@ static switch_status_t setup_formats(switch_memory_pool_t *pool)
skip = 0;
info.format = m;
sf_command(NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof(info));
+ if (!exten_is_allowed(info.extension)) {
+ continue;
+ }
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "%s (extension \"%s\")\n", info.name, info.extension);
for (x = 0; x < len; x++) {
if (supported_formats[x] == info.extension) {
@@ -470,7 +569,9 @@ static switch_status_t setup_formats(switch_memory_pool_t *pool)
}
}
for (m = 0; m < exlen; m++) {
- supported_formats[len++] = extras[m];
+ if (exten_is_allowed(extras[m])) {
+ supported_formats[len++] = extras[m];
+ }
}
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_NOTICE, "================================================================================\n");
@@ -478,12 +579,49 @@ static switch_status_t setup_formats(switch_memory_pool_t *pool)
return SWITCH_STATUS_SUCCESS;
}
+#define SNDFILE_DEBUG_SYNTAX ""
+SWITCH_STANDARD_API(mod_sndfile_debug)
+{
+ if (zstr(cmd)) {
+ stream->write_function(stream, "-USAGE: %s\n", SNDFILE_DEBUG_SYNTAX);
+ } else {
+ if (!strcasecmp(cmd, "on")) {
+ globals.debug = 1;
+ stream->write_function(stream, "Sndfile Debug: on\n");
+ } else if (!strcasecmp(cmd, "off")) {
+ globals.debug = 0;
+ stream->write_function(stream, "Sndfile Debug: off\n");
+ } else {
+ stream->write_function(stream, "-USAGE: %s\n", SNDFILE_DEBUG_SYNTAX);
+ }
+ }
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_MODULE_LOAD_FUNCTION(mod_sndfile_load)
{
switch_file_interface_t *file_interface;
+ switch_api_interface_t *commands_api_interface;
+ char *cf = "sndfile.conf";
+ switch_xml_t cfg, xml, settings, param;
+
+ memset(&globals, 0, sizeof(globals));
switch_core_hash_init(&globals.format_hash);
+ if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+ if ((settings = switch_xml_child(cfg, "settings"))) {
+ for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+ char *var = (char *) switch_xml_attr_soft(param, "name");
+ char *val = (char *) switch_xml_attr_soft(param, "value");
+ if (!strcasecmp(var, "allowed-extensions") && val) {
+ globals.allowed_extensions_count = switch_separate_string(val, ',', globals.allowed_extensions, (sizeof(globals.allowed_extensions) / sizeof(globals.allowed_extensions[0])));
+ }
+ }
+ }
+ switch_xml_free(xml);
+ }
+
if (setup_formats(pool) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
@@ -502,6 +640,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_sndfile_load)
file_interface->file_set_string = sndfile_file_set_string;
file_interface->file_get_string = sndfile_file_get_string;
+ SWITCH_ADD_API(commands_api_interface, "sndfile_debug", "Set sndfile debug", mod_sndfile_debug, SNDFILE_DEBUG_SYNTAX);
+
+ switch_console_set_complete("add sndfile_debug on");
+ switch_console_set_complete("add sndfile_debug off");
+
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
diff --git a/src/mod/formats/mod_sndfile/test/sounds/hello_stereo.wav b/src/mod/formats/mod_sndfile/test/sounds/hello_stereo.wav
new file mode 100644
index 0000000000..83e8388948
Binary files /dev/null and b/src/mod/formats/mod_sndfile/test/sounds/hello_stereo.wav differ
diff --git a/src/mod/formats/mod_sndfile/test/sounds/hi.wav b/src/mod/formats/mod_sndfile/test/sounds/hi.wav
new file mode 100644
index 0000000000..fec0d6b073
Binary files /dev/null and b/src/mod/formats/mod_sndfile/test/sounds/hi.wav differ
diff --git a/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml b/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml
new file mode 100644
index 0000000000..c29aed8991
--- /dev/null
+++ b/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml.fsxml b/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml.fsxml
new file mode 100644
index 0000000000..c29aed8991
--- /dev/null
+++ b/src/mod/formats/mod_sndfile/test/test_conf/freeswitch.xml.fsxml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mod/formats/mod_sndfile/test/test_formats_and_muxing/freeswitch.xml b/src/mod/formats/mod_sndfile/test/test_formats_and_muxing/freeswitch.xml
new file mode 100644
index 0000000000..39178db499
--- /dev/null
+++ b/src/mod/formats/mod_sndfile/test/test_formats_and_muxing/freeswitch.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/src/mod/formats/mod_sndfile/test/test_sndfile.c b/src/mod/formats/mod_sndfile/test/test_sndfile.c
new file mode 100644
index 0000000000..a9783ffa15
--- /dev/null
+++ b/src/mod/formats/mod_sndfile/test/test_sndfile.c
@@ -0,0 +1,451 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, 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):
+ * Dragos Oancea
+ *
+ * test_sndfile.c -- tests mod_sndfile
+ *
+ */
+#include
+#include
+
+#include
+
+/* Media files used:
+ *
+ * 1. hi.wav
+ *
+ * General
+ * Complete name : test/sounds/hi.wav
+ * Format : Wave
+ * File size : 67.2 KiB
+ * Duration : 2 s 150 ms
+ * Overall bit rate mode : Constant
+ * Overall bit rate : 256 kb/s
+ *
+ * Audio
+ * Format : PCM
+ * Format settings, Endianness : Little
+ * Format settings, Sign : Signed
+ * Codec ID : 1
+ * Duration : 2 s 150 ms
+ * Bit rate mode : Constant
+ * Bit rate : 256 kb/s
+ * Channel(s) : 1 channel
+ * Sampling rate : 16.0 kHz
+ * Bit depth : 16 bits
+ * Stream size : 67.2 KiB (100%)
+ *
+ *
+ * 2. hello_stereo.wav
+ *
+ *
+ * General
+ * Complete name : sounds/hello_stereo.wav
+ * Format : Wave
+ * File size : 220 KiB
+ * Duration : 1 s 277 ms
+ * Overall bit rate mode : Constant
+ * Overall bit rate : 1 412 kb/s
+ *
+ * Audio
+ * Format : PCM
+ * Format settings, Endianness : Little
+ * Format settings, Sign : Signed
+ * Codec ID : 1
+ * Duration : 1 s 277 ms
+ * Bit rate mode : Constant
+ * Bit rate : 1 411.2 kb/s
+ * Channel(s) : 2 channels
+ * Sampling rate : 44.1 kHz
+ * Bit depth : 16 bits
+ * Stream size : 220 KiB (100%)
+ *
+
+*/
+
+char *extensions[] = {
+ "aiff", "au", "avr", "caf",
+ "flac", "htk", "iff", "mat",
+ "mpc", "paf", "pvf", "rf64",
+ "sd2", "sds", "sf", "voc",
+ "w64", "wav", "wve", "xi",
+ "raw", "r8", "r16", "r24",
+ "r32", "ul", "ulaw", "al",
+ "alaw", "gsm", "vox", "oga", "ogg"};
+
+FST_CORE_BEGIN("test_formats_and_muxing")
+{
+ FST_SUITE_BEGIN(test_sndfile)
+ {
+ FST_SETUP_BEGIN()
+ {
+ fst_requires_module("mod_loopback");
+ fst_requires_module("mod_sndfile");
+ }
+ FST_SETUP_END()
+
+ FST_TEARDOWN_BEGIN()
+ {
+ }
+ FST_TEARDOWN_END()
+
+ FST_TEST_BEGIN(sndfile_write_read_mono)
+ {
+ /* play mono, record mono, open mono */
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ static char play_filename[] = "../sounds/hi.wav";
+ char path[4096];
+ switch_file_handle_t fh = { 0 };
+ int16_t *audiobuf;
+ switch_size_t len, rd;
+ char *recording;
+ int i, exlen, timeout_sec = 2, duration = 3000; /*ms*/
+ switch_stream_handle_t stream = { 0 };
+
+ sprintf(path, "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, play_filename);
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions) / sizeof(extensions[0]));
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ for (i = 0; i < exlen; i++) {
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions[i]);
+
+ recording = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions[i]);
+ status = switch_ivr_record_session(session, recording, duration, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_ivr_play_file(session, NULL, path, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_sleep(1000 * duration); // wait for audio to finish playing
+
+ switch_ivr_stop_record_session(session, "all");
+
+ status = switch_core_file_open(&fh, recording, 1, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ rd = 320; // samples
+ len = rd * sizeof(*audiobuf);
+ switch_zmalloc(audiobuf, len);
+
+ status = switch_core_file_read(&fh, audiobuf, &rd);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_check(rd = 320); // check that we read the wanted number of samples
+
+ status = switch_core_file_close(&fh);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ switch_safe_free(audiobuf);
+
+ unlink(recording);
+
+ switch_safe_free(recording);
+
+ switch_sleep(1000000);
+ }
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+ }
+
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(sndfile_write_read_m2s)
+ {
+ /* play mono file, record mono, open stereo */
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ static char play_filename[] = "../sounds/hi.wav";
+ char path[4096];
+ switch_file_handle_t fh = { 0 };
+ int16_t *audiobuf;
+ switch_size_t len, rd;
+ char *recording;
+ int i, exlen, channels = 2, timeout_sec = 2, duration = 3000; /*ms*/
+ switch_stream_handle_t stream = { 0 };
+
+ sprintf(path, "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, play_filename);
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions) / sizeof(extensions[0]));
+
+ for (i = 0; i < exlen; i++) {
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions[i]);
+
+ recording = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions[i]);
+
+ status = switch_ivr_record_session(session, recording, duration, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_ivr_play_file(session, NULL, path, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_sleep(1000 * duration); // wait for audio to finish playing
+
+ switch_ivr_stop_record_session(session, "all");
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+
+ status = switch_core_file_open(&fh, recording, channels, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ rd = 320; // samples
+ len = rd * sizeof(*audiobuf) * channels;
+ switch_zmalloc(audiobuf, len);
+
+ status = switch_core_file_read(&fh, audiobuf, &rd);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_check(rd = 320); // check that we read the wanted number of samples
+
+ status = switch_core_file_close(&fh);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ switch_safe_free(audiobuf);
+
+ unlink(recording);
+
+ switch_safe_free(recording);
+
+ switch_sleep(1000000);
+ }
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(sndfile_write_read_s2m)
+ {
+ /* play stereo wav, record stereo, open stereo file */
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ static char play_filename[] = "../sounds/hello_stereo.wav";
+ char path[4096];
+ switch_file_handle_t fh = { 0 };
+ int16_t *audiobuf;
+ switch_size_t len, rd;
+ char *recording, *rec_path;
+ int i, exlen, channels = 2, timeout_sec = 2, duration = 3000; /*ms*/
+ switch_stream_handle_t stream = { 0 };
+
+ sprintf(path, "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, play_filename);
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions) / sizeof(extensions[0]));
+
+ for (i = 0; i < exlen; i++) {
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions[i]);
+
+ rec_path = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions[i]);
+ recording = switch_mprintf("{force_channels=2}%s", rec_path);
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_set_variable(channel, "enable_file_write_buffering", "true");
+
+ status = switch_ivr_record_session(session, recording, duration, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_ivr_play_file(session, NULL, path, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_sleep(1000 * duration); // wait for audio to finish playing
+
+ switch_ivr_stop_record_session(session, "all");
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+
+ status = switch_core_file_open(&fh, recording, channels, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ rd = 320; // samples
+ len = rd * sizeof(*audiobuf) * channels;
+ switch_zmalloc(audiobuf, len);
+
+ status = switch_core_file_read(&fh, audiobuf, &rd);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_check(rd = 320); // check that we read the wanted number of samples
+
+ status = switch_core_file_close(&fh);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ switch_safe_free(audiobuf);
+
+ unlink(rec_path);
+
+ switch_safe_free(rec_path);
+ switch_safe_free(recording);
+
+ switch_sleep(1000000);
+ }
+ }
+
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(sndfile_write_read_stereo)
+ {
+ /* play stereo wav, record stereo, open stereo file */
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ static char play_filename[] = "../sounds/hello_stereo.wav";
+ char path[4096];
+ switch_file_handle_t fh = { 0 };
+ int16_t *audiobuf;
+ switch_size_t len, rd;
+ char *recording, *rec_path;
+ int i, exlen, channels = 2, timeout_sec = 2, duration = 3000; /*ms*/
+ switch_stream_handle_t stream = { 0 };
+
+ sprintf(path, "%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, play_filename);
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions) / sizeof(extensions[0]));
+
+ for (i = 0; i < exlen; i++) {
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions[i]);
+
+ rec_path = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions[i]);
+ recording = switch_mprintf("{force_channels=2}%s", rec_path);
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_set_variable(channel, "RECORD_STEREO", "true");
+ switch_channel_set_variable(channel, "enable_file_write_buffering", "true");
+
+ status = switch_ivr_record_session(session, recording, duration, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ status = switch_ivr_play_file(session, NULL, path, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ switch_sleep(1000 * duration); // wait for audio to finish playing
+
+ switch_ivr_stop_record_session(session, "all");
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+
+ status = switch_core_file_open(&fh, recording, channels, 8000, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ rd = 320; // samples
+ len = rd * sizeof(*audiobuf) * channels;
+ switch_zmalloc(audiobuf, len);
+
+ status = switch_core_file_read(&fh, audiobuf, &rd);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+ fst_check(rd = 320); // check that we read the wanted number of samples
+
+ status = switch_core_file_close(&fh);
+ fst_requires(status == SWITCH_STATUS_SUCCESS);
+
+ switch_safe_free(audiobuf);
+
+ unlink(rec_path);
+
+ switch_safe_free(rec_path);
+ switch_safe_free(recording);
+
+ switch_sleep(1000000);
+ }
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(unload_mod_sndfile)
+ {
+ const char *err = NULL;
+ switch_sleep(1000000);
+ fst_check(switch_loadable_module_unload_module((char *)"../.libs", (char *)"mod_sndfile", SWITCH_TRUE, &err) == SWITCH_STATUS_SUCCESS);
+ }
+ FST_TEST_END()
+ }
+ FST_SUITE_END()
+}
+FST_CORE_END()
diff --git a/src/mod/formats/mod_sndfile/test/test_sndfile_conf.c b/src/mod/formats/mod_sndfile/test/test_sndfile_conf.c
new file mode 100644
index 0000000000..5150591922
--- /dev/null
+++ b/src/mod/formats/mod_sndfile/test/test_sndfile_conf.c
@@ -0,0 +1,154 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, 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):
+ * Dragos Oancea
+ *
+ * test_sndfile_conf.c -- tests mod_sndfile config
+ *
+ */
+#include
+#include
+
+#include
+
+char *extensions_will_fail[] = { // not allowed through conf file.
+ "ul", "gsm", "vox", "ogg"
+};
+
+char *extensions_will_succeed[] = { // allowed through conf file.
+ "wav", "raw", "r8", "r16"
+};
+
+FST_CORE_BEGIN("test_conf")
+{
+ FST_SUITE_BEGIN(test_sndfile)
+ {
+ FST_SETUP_BEGIN()
+ {
+ fst_requires_module("mod_loopback");
+ fst_requires_module("mod_sndfile");
+ }
+ FST_SETUP_END()
+
+ FST_TEARDOWN_BEGIN()
+ {
+ }
+ FST_TEARDOWN_END()
+
+ FST_TEST_BEGIN(sndfile_exten_not_allowed)
+ {
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ char *recording;
+ int i, exlen, timeout_sec = 2;
+ switch_stream_handle_t stream = { 0 };
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions_will_fail) / sizeof(extensions_will_fail[0]));
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ for (i = 0; i < exlen; i++) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions_will_fail[i]);
+
+ recording = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions_will_fail[i]);
+ status = switch_ivr_record_session(session, recording, 3000, NULL);
+ fst_check(status == SWITCH_STATUS_GENERR);
+ if (status == SWITCH_STATUS_SUCCESS) {
+ // not expected
+ unlink(recording);
+ }
+
+ switch_safe_free(recording);
+ }
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+
+ switch_sleep(1000000);
+ }
+ FST_TEST_END()
+ FST_TEST_BEGIN(sndfile_exten_allowed)
+ {
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ switch_status_t status;
+ switch_call_cause_t cause;
+ char *recording;
+ int i, exlen, timeout_sec = 2;
+ switch_stream_handle_t stream = { 0 };
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ switch_api_execute("sndfile_debug", "on", session, &stream);
+
+ switch_safe_free(stream.data);
+
+ exlen = (sizeof(extensions_will_succeed) / sizeof(extensions_will_succeed[0]));
+
+ status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", timeout_sec, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL);
+ fst_requires(session);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ for (i = 0; i < exlen; i++) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Testing media file extension: [%s]\n", extensions_will_succeed[i]);
+
+ recording = switch_mprintf("/tmp/%s.%s", switch_core_session_get_uuid(session), extensions_will_succeed[i]);
+ status = switch_ivr_record_session(session, recording, 3000, NULL);
+ fst_check(status == SWITCH_STATUS_SUCCESS);
+
+ unlink(recording);
+
+ switch_safe_free(recording);
+ }
+
+ channel = switch_core_session_get_channel(session);
+ fst_requires(channel);
+
+ switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+ fst_check(!switch_channel_ready(channel));
+
+ switch_core_session_rwunlock(session);
+
+ switch_sleep(1000000);
+ }
+ FST_TEST_END()
+ }
+ FST_SUITE_END()
+}
+FST_CORE_END()