[mod_sndfile] fix playing "raw" and other formats ("r8", "r16", "r24", "r32"), add unit-test

[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)
This commit is contained in:
Dragos Oancea 2020-01-09 20:17:42 +00:00
parent 78c7dd2a37
commit 08317fc5c3
11 changed files with 872 additions and 13 deletions

View File

@ -0,0 +1,9 @@
<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>

View File

@ -0,0 +1,9 @@
<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>

View File

@ -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

View File

@ -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 "<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;
}
@ -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;
}

Binary file not shown.

View File

@ -0,0 +1,27 @@
<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>

View File

@ -0,0 +1,27 @@
<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>

View File

@ -0,0 +1,21 @@
<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>

View File

@ -0,0 +1,451 @@
/*
* 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()

View File

@ -0,0 +1,154 @@
/*
* 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()