[mod_sndfile] add stereo to mono support for file formats that need it (eg: gsm, vox), add unit test for stereo
[mod_sndfile] mono to stereo and stereo to mono unit tests.
[unit-tests] mod_sndfile: don't wait for recording file to be flushed to disk by the core, use switch_ivr_stop_record_session()
[mod_sndfile] fix format "wve" (Psion Series 3)
[mod_sndfile] fix format "htk" (Hidden Markov Model Tool Kit)
[mod_sndfile] fix file format "iff" (AIFF)
[mod_sndfile] fix file format "xi" (FastTracker 2)
[mod_sndfile] fix file format "sds" (Midi Sample Dump Standard)
[uni-tests] add more audio file extensions to sndfile unit tests.
[mod_sndfile] add config file support (with one param currently: "allowed-extensions")
[mod_sndfile] add sample file sndfile.conf.xml to 'testing' and 'vanilla' cfg trees.
[mod_sndfile] free() cfg xml
[unit-tests] mod_sndfile: unload module test.
[unit-tests] mod_sndfile: add conf file unit-test (param that allows only certain file extensions)
[unit-tests] adjusts Makefile.am for new CI (tests)
[unit-tests] adjust path to sound file.
[unit-tests] [mod_sndfile] remove Makefile.am from test/
[unit-tests] [mod_sndfile] fix build (Andrey)
--- /dev/null
+<configuration name="sndfile.conf">
+ <settings>
+ <!-- Allow only these file extensions. Default: allow all sndfile provided extensions + FS custom extra -->
+ <!--
+ <param name="allowed-extensions" value="wav,raw,r8,r16"/>
+ -->
+ </settings>
+</configuration>
+
--- /dev/null
+<configuration name="sndfile.conf">
+ <settings>
+ <!-- Allow only these file extensions. Default: allow all sndfile provided extensions + FS custom extra -->
+ <!--
+ <param name="allowed-extensions" value="wav,raw,r8,r16"/>
+ -->
+ </settings>
+</configuration>
+
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
static struct {
switch_hash_t *format_hash;
+ int debug;
+ char *allowed_extensions[100];
+ int allowed_extensions_count;
} globals;
struct format_map {
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;
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) {
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;
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;
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) {
}
}
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");
return SWITCH_STATUS_SUCCESS;
}
+#define SNDFILE_DEBUG_SYNTAX "<on|off>"
+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;
}
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;
}
--- /dev/null
+<document type="freeswitch/xml">
+
+ <section name="configuration" description="Various Configuration">
+ <configuration name="modules.conf" description="Modules">
+ <modules>
+ <load module="mod_loopback"/>
+ <load module="mod_sndfile"/>
+ </modules>
+ </configuration>
+ <configuration name="sndfile.conf">
+ <settings>
+ <!-- Allow only these file extensions. Default: allow all sndfile provided extensions + FS custom extra -->
+ <param name="allowed-extensions" value="wav,raw,r8,r16"/>
+ </settings>
+ </configuration>
+ </section>
+
+ <section name="dialplan" description="Regex/XML Dialplan">
+ <context name="default">
+ <extension name="sample">
+ <condition>
+ <action application="info"/>
+ </condition>
+ </extension>
+ </context>
+ </section>
+</document>
--- /dev/null
+<document type="freeswitch/xml">
+
+ <section name="configuration" description="Various Configuration">
+ <configuration name="modules.conf" description="Modules">
+ <modules>
+ <load module="mod_loopback"/>
+ <load module="mod_sndfile"/>
+ </modules>
+ </configuration>
+ <configuration name="sndfile.conf">
+ <settings>
+ <!-- Allow only these file extensions. Default: allow all sndfile provided extensions + FS custom extra -->
+ <param name="allowed-extensions" value="wav,raw,r8,r16"/>
+ </settings>
+ </configuration>
+ </section>
+
+ <section name="dialplan" description="Regex/XML Dialplan">
+ <context name="default">
+ <extension name="sample">
+ <condition>
+ <action application="info"/>
+ </condition>
+ </extension>
+ </context>
+ </section>
+</document>
--- /dev/null
+<document type="freeswitch/xml">
+
+ <section name="configuration" description="Various Configuration">
+ <configuration name="modules.conf" description="Modules">
+ <modules>
+ <load module="mod_loopback"/>
+ <load module="mod_sndfile"/>
+ </modules>
+ </configuration>
+ </section>
+
+ <section name="dialplan" description="Regex/XML Dialplan">
+ <context name="default">
+ <extension name="sample">
+ <condition>
+ <action application="info"/>
+ </condition>
+ </extension>
+ </context>
+ </section>
+</document>
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dragos Oancea <dragos@signalwire.com>
+ *
+ * test_sndfile.c -- tests mod_sndfile
+ *
+ */
+#include <switch.h>
+#include <stdlib.h>
+
+#include <test/switch_test.h>
+
+/* 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()
--- /dev/null
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * 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 <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dragos Oancea <dragos@signalwire.com>
+ *
+ * test_sndfile_conf.c -- tests mod_sndfile config
+ *
+ */
+#include <switch.h>
+#include <stdlib.h>
+
+#include <test/switch_test.h>
+
+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()