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()