diff --git a/mediastreamer2 b/mediastreamer2
index c01ab3c2d..128815537 160000
--- a/mediastreamer2
+++ b/mediastreamer2
@@ -1 +1 @@
-Subproject commit c01ab3c2df96a2aad53edde9b4759344ed3a5bad
+Subproject commit 128815537044ea17808a0a5843b2762364f8203d
diff --git a/tester/CMakeLists.txt b/tester/CMakeLists.txt
index 6b2fa81d2..39643bfba 100644
--- a/tester/CMakeLists.txt
+++ b/tester/CMakeLists.txt
@@ -22,6 +22,7 @@
set(SOURCE_FILES
accountmanager.c
+ audio_bypass_tester.c
call_tester.c
complex_sip_call_tester.c
dtmf_tester.c
diff --git a/tester/Makefile.am b/tester/Makefile.am
index 4d3df1a78..aca1974cb 100644
--- a/tester/Makefile.am
+++ b/tester/Makefile.am
@@ -1,6 +1,7 @@
TESTER_SOUNDS = sounds/ahbahouaismaisbon.wav \
sounds/hello8000.wav \
+ sounds/hello44100.wav \
sounds/oldphone.wav \
sounds/sintel_trailer_opus_h264.mkv \
sounds/sintel_trailer_pcmu_h264.mkv \
@@ -112,6 +113,7 @@ lib_LTLIBRARIES = liblinphonetester.la
liblinphonetester_la_SOURCES = \
accountmanager.c \
+ audio_bypass_tester.c \
call_tester.c \
complex_sip_call_tester.c \
dtmf_tester.c \
diff --git a/tester/audio_bypass_tester.c b/tester/audio_bypass_tester.c
new file mode 100644
index 000000000..3f248f4d8
--- /dev/null
+++ b/tester/audio_bypass_tester.c
@@ -0,0 +1,501 @@
+/*
+ liblinphone_tester - liblinphone test suite
+ Copyright (C) 2013 Belledonne Communications SARL
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "liblinphone_tester.h"
+#include "private.h"
+#include "audio_bypass_wav_header.h" // This is a copy of mediastreamer2/src/audiofilters/wav_header.h
+
+/**********************************************************************
+ * This is a (simpler) copy of msfileplay filter in mediastreamer2 *
+ *********************************************************************/
+
+struct _PlayerData{
+ int fd;
+ MSPlayerState state;
+ int rate;
+ int nchannels;
+ int hsize;
+ int loop_after;
+ int pause_time;
+ int count;
+ int samplesize;
+ char *mime;
+ uint32_t ts;
+ bool_t swap;
+ bool_t is_raw;
+};
+
+typedef struct _PlayerData PlayerData;
+
+static void audio_bypass_snd_read_init(MSFilter *f) {
+ PlayerData *d=ms_new0(PlayerData,1);
+ d->fd=-1;
+ d->state=MSPlayerClosed;
+ d->swap=TRUE;
+ d->rate=44100;
+ d->nchannels=1;
+ d->samplesize=2;
+ d->mime = "L16";
+ d->hsize=0;
+ d->loop_after=-1; /*by default, don't loop*/
+ d->pause_time=0;
+ d->count=0;
+ d->ts=0;
+ d->is_raw=TRUE;
+ f->data=d;
+}
+
+int ms_read_wav_header_from_fd(wave_header_t *header,int fd){
+ int count;
+ int skip;
+ int hsize=0;
+ riff_t *riff_chunk=&header->riff_chunk;
+ format_t *format_chunk=&header->format_chunk;
+ data_t *data_chunk=&header->data_chunk;
+
+ unsigned long len=0;
+
+ len = read(fd, (char*)riff_chunk, sizeof(riff_t)) ;
+ if (len != sizeof(riff_t)){
+ goto not_a_wav;
+ }
+
+ if (0!=strncmp(riff_chunk->riff, "RIFF", 4) || 0!=strncmp(riff_chunk->wave, "WAVE", 4)){
+ goto not_a_wav;
+ }
+
+ len = read(fd, (char*)format_chunk, sizeof(format_t)) ;
+ if (len != sizeof(format_t)){
+ ms_warning("Wrong wav header: cannot read file");
+ goto not_a_wav;
+ }
+
+ if ((skip=le_uint32(format_chunk->len)-0x10)>0)
+ {
+ lseek(fd,skip,SEEK_CUR);
+ }
+ hsize=sizeof(wave_header_t)-0x10+le_uint32(format_chunk->len);
+
+ count=0;
+ do{
+ len = read(fd, data_chunk, sizeof(data_t)) ;
+ if (len != sizeof(data_t)){
+ ms_warning("Wrong wav header: cannot read file");
+ goto not_a_wav;
+ }
+ if (strncmp(data_chunk->data, "data", 4)!=0){
+ ms_warning("skipping chunk=%c%c%c%c len=%i", data_chunk->data[0],data_chunk->data[1],data_chunk->data[2],data_chunk->data[3], data_chunk->len);
+ lseek(fd,le_uint32(data_chunk->len),SEEK_CUR);
+ count++;
+ hsize+=len+le_uint32(data_chunk->len);
+ }else{
+ hsize+=len;
+ break;
+ }
+ }while(count<30);
+ return hsize;
+
+ not_a_wav:
+ /*rewind*/
+ lseek(fd,0,SEEK_SET);
+ return -1;
+}
+
+static int read_wav_header(PlayerData *d){
+ wave_header_t header;
+ format_t *format_chunk=&header.format_chunk;
+ int ret=ms_read_wav_header_from_fd(&header,d->fd);
+
+ d->samplesize=le_uint16(format_chunk->blockalign)/d->nchannels;
+ d->hsize=ret;
+
+ #ifdef WORDS_BIGENDIAN
+ if (le_uint16(format_chunk->blockalign)==le_uint16(format_chunk->channel) * 2)
+ d->swap=TRUE;
+ #endif
+ d->is_raw=FALSE;
+ return 0;
+}
+
+static void audio_bypass_snd_read_preprocess(MSFilter *f) {
+ PlayerData *d=(PlayerData*)f->data;
+ int fd;
+ const char *file=bc_tester_res("sounds/hello44100.wav");
+
+ if ((fd=open(file,O_RDONLY|O_BINARY))==-1){
+ ms_warning("MSFilePlayer[%p]: failed to open %s: %s",f,file,strerror(errno));
+ return;
+ }
+
+ d->state=MSPlayerPaused;
+ d->fd=fd;
+ d->ts=0;
+ if (read_wav_header(d)!=0 && strstr(file,".wav")){
+ ms_warning("File %s has .wav extension but wav header could be found.",file);
+ }
+ ms_filter_notify_no_arg(f,MS_FILTER_OUTPUT_FMT_CHANGED);
+ ms_message("MSFilePlayer[%p]: %s opened: rate=%i,channel=%i",f,file,d->rate,d->nchannels);
+
+ if (d->state==MSPlayerPaused)
+ d->state=MSPlayerPlaying;
+ return;
+}
+
+static void swap_bytes(unsigned char *bytes, int len){
+ int i;
+ unsigned char tmp;
+ for(i=0;idata;
+ int nsamples=(f->ticker->interval*d->rate*d->nchannels)/1000;
+ int bytes;
+ /*send an even number of samples each tick. At 22050Hz the number of samples per 10 ms chunk is odd.
+ Odd size buffer of samples cause troubles to alsa. Fixing in alsa is difficult, so workaround here.
+ */
+ if (nsamples & 0x1 ) { //odd number of samples
+ if (d->count & 0x1 )
+ nsamples++;
+ else
+ nsamples--;
+ }
+ bytes=nsamples*d->samplesize;
+ d->count++;
+ ms_filter_lock(f);
+ if (d->state==MSPlayerPlaying){
+ {
+ int err;
+ mblk_t *om=allocb(bytes,0);
+ if (d->pause_time>0){
+ err=bytes;
+ memset(om->b_wptr,0,bytes);
+ d->pause_time-=f->ticker->interval;
+ }else{
+ err=read(d->fd,om->b_wptr,bytes);
+ if (d->swap) swap_bytes(om->b_wptr,bytes);
+ }
+ if (err>=0){
+ if (err!=0){
+ if (errb_wptr+err,0,bytes-err);
+ om->b_wptr+=bytes;
+ mblk_set_timestamp_info(om,d->ts);
+ d->ts+=nsamples;
+ ms_queue_put(f->outputs[0],om);
+ }else freemsg(om);
+ if (errfd,d->hsize,SEEK_SET);
+
+ /* special value for playing file only once */
+ if (d->loop_after<0)
+ {
+ d->state=MSPlayerPaused;
+ ms_filter_unlock(f);
+ return;
+ }
+
+ if (d->loop_after>=0){
+ d->pause_time=d->loop_after;
+ }
+ }
+ }else{
+ ms_warning("Fail to read %i bytes: %s",bytes,strerror(errno));
+ }
+ }
+ }
+ ms_filter_unlock(f);
+}
+
+static void audio_bypass_snd_read_postprocess(MSFilter *f) {
+ PlayerData *d=(PlayerData*)f->data;
+ ms_filter_lock(f);
+ if (d->state!=MSPlayerClosed){
+ d->state=MSPlayerPaused;
+ lseek(d->fd,d->hsize,SEEK_SET);
+ }
+ ms_filter_unlock(f);
+ if (d->fd!=-1) close(d->fd);
+ d->fd=-1;
+ d->state=MSPlayerClosed;
+}
+
+static void audio_bypass_snd_read_uninit(MSFilter *f) {
+ PlayerData *d=(PlayerData*)f->data;
+ ms_free(d);
+}
+
+static int audio_bypass_snd_read_set_sample_rate(MSFilter *f, void *arg) { // This is to prevent ms2 to put a resampler between this filter and the rtpsend
+ return 0;
+}
+
+static int audio_bypass_snd_read_set_nchannels(MSFilter *f, void *arg) { // This is to prevent ms2 to put a resampler between this filter and the rtpsend
+ return 0;
+}
+
+static int audio_bypass_snd_read_get_sample_rate(MSFilter *f, void *arg) {
+ int *sample_rate = (int *)arg;
+ *sample_rate = 44100;
+ return 0;
+}
+
+static int audio_bypass_snd_read_get_nchannels(MSFilter *f, void *arg) {
+ int *nchannels = (int *)arg;
+ *nchannels = 1;
+ return 0;
+}
+
+static int audio_bypass_snd_read_get_fmt(MSFilter *f, void *arg) {
+ MSPinFormat *pinFmt = (MSPinFormat *)arg;
+ pinFmt->fmt = ms_factory_get_audio_format(f->factory, "L16", 44100, 1, NULL);
+ return 0;
+}
+
+static MSFilterMethod audio_bypass_snd_read_methods[] = {
+ { MS_FILTER_SET_SAMPLE_RATE, audio_bypass_snd_read_set_sample_rate },
+ { MS_FILTER_SET_NCHANNELS, audio_bypass_snd_read_set_nchannels },
+ { MS_FILTER_GET_SAMPLE_RATE, audio_bypass_snd_read_get_sample_rate },
+ { MS_FILTER_GET_NCHANNELS, audio_bypass_snd_read_get_nchannels },
+ { MS_FILTER_GET_OUTPUT_FMT, audio_bypass_snd_read_get_fmt },
+ { 0, NULL }
+};
+
+MSFilterDesc audio_bypass_snd_read_desc = {
+ MS_FILTER_PLUGIN_ID,
+ "AudioBypassReader",
+ "audio bypass source",
+ MS_FILTER_OTHER,
+ NULL,
+ 0,
+ 1,
+ audio_bypass_snd_read_init,
+ audio_bypass_snd_read_preprocess,
+ audio_bypass_snd_read_process,
+ audio_bypass_snd_read_postprocess,
+ audio_bypass_snd_read_uninit,
+ audio_bypass_snd_read_methods
+};
+
+static void audio_bypass_snd_write_init(MSFilter *f) {
+
+}
+
+static void audio_bypass_snd_write_preprocess(MSFilter *f) {
+
+}
+
+static void audio_bypass_snd_write_process(MSFilter *f) {
+ mblk_t *m = ms_queue_get(f->inputs[0]);
+ ms_free(m);
+}
+
+static void audio_bypass_snd_write_postprocess(MSFilter *f) {
+
+}
+
+static void audio_bypass_snd_write_uninit(MSFilter *f) {
+
+}
+
+static int audio_bypass_snd_write_set_sample_rate(MSFilter *f, void *arg) { // This is to prevent ms2 to put a resampler between this filter and the rtprecv
+ return 0;
+}
+
+static int audio_bypass_snd_write_set_nchannels(MSFilter *f, void *arg) { // This is to prevent ms2 to put a resampler between this filter and the rtprecv
+ return 0;
+}
+
+static int audio_bypass_snd_write_get_sample_rate(MSFilter *f, void *arg) {
+ int *sample_rate = (int*)arg;
+ *sample_rate = 44100;
+ return 0;
+}
+
+static int audio_bypass_snd_write_get_nchannels(MSFilter *obj, void *arg) {
+ int *nchannels = (int*)arg;
+ *nchannels = 1;
+ return 0;
+}
+
+static int audio_bypass_snd_write_get_fmt(MSFilter *f, void *arg) {
+ MSPinFormat *pinFmt = (MSPinFormat *)arg;
+ pinFmt->fmt = ms_factory_get_audio_format(f->factory, "L16", 44100, 1, NULL);
+ return 0;
+}
+
+static MSFilterMethod audio_bypass_snd_write_methods[] = {
+ { MS_FILTER_SET_SAMPLE_RATE, audio_bypass_snd_write_set_sample_rate },
+ { MS_FILTER_SET_NCHANNELS, audio_bypass_snd_write_set_nchannels },
+ { MS_FILTER_GET_SAMPLE_RATE, audio_bypass_snd_write_get_sample_rate },
+ { MS_FILTER_GET_NCHANNELS, audio_bypass_snd_write_get_nchannels },
+ { MS_FILTER_GET_OUTPUT_FMT, audio_bypass_snd_write_get_fmt },
+ { 0, NULL }
+};
+
+MSFilterDesc audio_bypass_snd_write_desc = {
+ MS_FILTER_PLUGIN_ID,
+ "AudioBypassWriter",
+ "audio bypass output",
+ MS_FILTER_OTHER,
+ NULL,
+ 1,
+ 0,
+ audio_bypass_snd_write_init,
+ audio_bypass_snd_write_preprocess,
+ audio_bypass_snd_write_process,
+ audio_bypass_snd_write_postprocess,
+ audio_bypass_snd_write_uninit,
+ audio_bypass_snd_write_methods
+};
+
+static MSFilter* audio_bypass_snd_card_create_reader(MSSndCard *sndcard) {
+ MSFactory *factory = ms_snd_card_get_factory(sndcard);
+ MSFilter *f = ms_factory_create_filter_from_desc(factory, &audio_bypass_snd_read_desc);
+ return f;
+}
+
+static MSFilter* audio_bypass_snd_card_create_writer(MSSndCard *sndcard) {
+ MSFactory *factory = ms_snd_card_get_factory(sndcard);
+ MSFilter *f = ms_factory_create_filter_from_desc(factory, &audio_bypass_snd_write_desc);
+ return f;
+}
+
+static void audio_bypass_snd_card_detect(MSSndCardManager *m);
+
+MSSndCardDesc audio_bypass_snd_card_desc = {
+ "audioBypass",
+ audio_bypass_snd_card_detect,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ audio_bypass_snd_card_create_reader,
+ audio_bypass_snd_card_create_writer,
+ NULL
+};
+
+static MSSndCard* create_audio_bypass_snd_card(void) {
+ MSSndCard* sndcard;
+ sndcard = ms_snd_card_new(&audio_bypass_snd_card_desc);
+ sndcard->data = NULL;
+ sndcard->name = ms_strdup("audio bypass sound card");
+ sndcard->capabilities = MS_SND_CARD_CAP_PLAYBACK | MS_SND_CARD_CAP_CAPTURE;
+ sndcard->latency = 0;
+ return sndcard;
+}
+
+#define AUDIO_BYPASS_SOUNDCARD "audioBypass: audio bypass sound card"
+
+static void audio_bypass_snd_card_detect(MSSndCardManager *m) {
+ ms_snd_card_manager_add_card(m, create_audio_bypass_snd_card());
+}
+
+static void only_enable_payload(LinphoneCore *lc, const char *mime, int rate, int channels) {
+ const MSList *elem = linphone_core_get_audio_codecs(lc);
+ PayloadType *pt;
+
+ for(; elem != NULL; elem = elem->next) {
+ pt = (PayloadType *)elem->data;
+ linphone_core_enable_payload_type(lc, pt, FALSE);
+ }
+ pt = linphone_core_find_payload_type(lc, mime, rate, channels);
+ if (BC_ASSERT_PTR_NOT_NULL(pt)) {
+ linphone_core_enable_payload_type(lc, pt, TRUE);
+ }
+}
+
+static void audio_bypass(void) {
+ LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
+ LinphoneCore *marie_lc = marie->lc;
+ MSFactory *marie_factory = linphone_core_get_ms_factory(marie_lc);
+ MSSndCardManager *marie_sndcard_manager = ms_factory_get_snd_card_manager(marie_factory);
+
+ LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_rc");
+ LinphoneCore *pauline_lc = pauline->lc;
+ MSFactory *pauline_factory = linphone_core_get_ms_factory(pauline_lc);
+ MSSndCardManager *pauline_sndcard_manager = ms_factory_get_snd_card_manager(pauline_factory);
+
+ bool_t call_ok;
+ char *hellopath = bc_tester_res("sounds/hello44100.wav");
+ char *recordpath = bc_tester_file("audiobypass-record.wav");
+ double similar=1;
+ const double threshold = 0.85;
+
+ lp_config_set_string(marie_lc->config, "sound", "features", "None");
+ lp_config_set_string(pauline_lc->config, "sound", "features", "None");
+
+ /*make sure the record file doesn't already exists, otherwise this test will append new samples to it*/
+ unlink(recordpath);
+
+ // Enable L16 audio codec
+ only_enable_payload(marie_lc, "L16", 44100, 1);
+ only_enable_payload(pauline_lc, "L16", 44100, 1);
+
+ // Add our custom sound card
+ ms_snd_card_manager_register_desc(marie_sndcard_manager, &audio_bypass_snd_card_desc);
+ ms_snd_card_manager_register_desc(pauline_sndcard_manager, &audio_bypass_snd_card_desc);
+ linphone_core_reload_sound_devices(marie_lc);
+ linphone_core_reload_sound_devices(pauline_lc);
+ linphone_core_set_playback_device(marie_lc, AUDIO_BYPASS_SOUNDCARD);
+ linphone_core_set_playback_device(pauline_lc, AUDIO_BYPASS_SOUNDCARD);
+ linphone_core_set_capture_device(marie_lc, AUDIO_BYPASS_SOUNDCARD);
+ linphone_core_set_capture_device(pauline_lc, AUDIO_BYPASS_SOUNDCARD);
+ BC_ASSERT_STRING_EQUAL(linphone_core_get_capture_device(marie_lc), AUDIO_BYPASS_SOUNDCARD);
+ BC_ASSERT_STRING_EQUAL(linphone_core_get_capture_device(pauline_lc), AUDIO_BYPASS_SOUNDCARD);
+ BC_ASSERT_STRING_EQUAL(linphone_core_get_playback_device(marie_lc), AUDIO_BYPASS_SOUNDCARD);
+ BC_ASSERT_STRING_EQUAL(linphone_core_get_playback_device(pauline_lc), AUDIO_BYPASS_SOUNDCARD);
+
+ linphone_core_use_files(pauline_lc, TRUE);
+ linphone_core_set_play_file(pauline_lc, NULL);
+ linphone_core_set_record_file(pauline_lc, recordpath);
+
+ call_ok = call(marie, pauline);
+ BC_ASSERT_TRUE(call_ok);
+ if (!call_ok) goto end;
+
+ BC_ASSERT_STRING_EQUAL(linphone_call_params_get_used_audio_codec(linphone_call_get_current_params(linphone_core_get_current_call(marie_lc)))->mime_type, "L16");
+
+ wait_for_until(pauline_lc, marie_lc, NULL, 0, 22000); //hello44100.wav is 22 seconds long
+ end_call(marie, pauline);
+
+ BC_ASSERT_EQUAL(ms_audio_diff(hellopath, recordpath, &similar, &audio_cmp_params, NULL, NULL), 0, int, "%d");
+ BC_ASSERT_GREATER(similar, threshold, double, "%g");
+ BC_ASSERT_LOWER(similar, 1.0, double, "%g");
+
+end:
+ bc_free(recordpath);
+ bc_free(hellopath);
+ linphone_core_manager_destroy(marie);
+ linphone_core_manager_destroy(pauline);
+}
+
+test_t audio_bypass_tests[] = {
+ TEST_NO_TAG("Audio Bypass", audio_bypass)
+};
+
+test_suite_t audio_bypass_suite = { "Audio Bypass", NULL, NULL,
+ liblinphone_tester_before_each, liblinphone_tester_after_each,
+ sizeof(audio_bypass_tests) / sizeof(audio_bypass_tests[0]), audio_bypass_tests };
diff --git a/tester/audio_bypass_wav_header.h b/tester/audio_bypass_wav_header.h
new file mode 100644
index 000000000..f86c87f6f
--- /dev/null
+++ b/tester/audio_bypass_wav_header.h
@@ -0,0 +1,139 @@
+/*
+mediastreamer2 library - modular sound and video processing and streaming
+Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef waveheader_h
+#define waveheader_h
+
+#include
+#include
+#include
+
+#ifdef _WIN32
+# include
+# ifndef R_OK
+# define R_OK 0x2
+# endif
+# ifndef W_OK
+# define W_OK 0x6
+# endif
+# ifndef F_OK
+# define F_OK 0x0
+# endif
+
+# ifndef S_IRUSR
+# define S_IRUSR S_IREAD
+# endif
+
+# ifndef S_IWUSR
+# define S_IWUSR S_IWRITE
+# endif
+
+# define open _open
+# define read _read
+# define write _write
+# define close _close
+# define access _access
+# define lseek _lseek
+#else /*_WIN32*/
+
+# ifndef O_BINARY
+# define O_BINARY 0
+# endif
+
+#endif /*!_WIN32*/
+
+#ifdef swap16
+#else
+/* all integer in wav header must be read in least endian order */
+static MS2_INLINE uint16_t swap16(uint16_t a)
+{
+ return ((a & 0xFF) << 8) | ((a & 0xFF00) >> 8);
+}
+#endif
+
+#ifdef swap32
+#else
+static MS2_INLINE uint32_t swap32(uint32_t a)
+{
+ return ((a & 0xFF) << 24) | ((a & 0xFF00) << 8) |
+ ((a & 0xFF0000) >> 8) | ((a & 0xFF000000) >> 24);
+}
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define le_uint32(a) (swap32((a)))
+#define le_uint16(a) (swap16((a)))
+#define le_int16(a) ( (int16_t) swap16((uint16_t)((a))) )
+#else
+#define le_uint32(a) (a)
+#define le_uint16(a) (a)
+#define le_int16(a) (a)
+#endif
+
+typedef struct _riff_t {
+ char riff[4] ; /* "RIFF" (ASCII characters) */
+ uint32_t len ; /* Length of package (binary, little endian) */
+ char wave[4] ; /* "WAVE" (ASCII characters) */
+} riff_t;
+
+/* The FORMAT chunk */
+
+typedef struct _format_t {
+ char fmt[4] ; /* "fmt_" (ASCII characters) */
+ uint32_t len ; /* length of FORMAT chunk (always 0x10) */
+ uint16_t type; /* codec type*/
+ uint16_t channel ; /* Channel numbers (0x01 = mono, 0x02 = stereo) */
+ uint32_t rate ; /* Sample rate (binary, in Hz) */
+ uint32_t bps ; /* Average Bytes Per Second */
+ uint16_t blockalign ; /*number of bytes per sample */
+ uint16_t bitpspl ; /* bits per sample */
+} format_t;
+
+/* The DATA chunk */
+
+typedef struct _data_t {
+ char data[4] ; /* "data" (ASCII characters) */
+ uint32_t len ; /* length of data */
+} data_t;
+
+typedef struct _wave_header_t
+{
+ riff_t riff_chunk;
+ format_t format_chunk;
+ data_t data_chunk;
+} wave_header_t;
+
+#ifndef _WIN32
+#define WAVE_FORMAT_PCM 0x0001
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003
+#define WAVE_FORMAT_ALAW 0x0006
+#define WAVE_FORMAT_MULAW 0x0007
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+#endif
+
+#define wave_header_get_format_type(header) le_uint16((header)->format_chunk.type)
+#define wave_header_get_rate(header) le_uint32((header)->format_chunk.rate)
+#define wave_header_get_channel(header) le_uint16((header)->format_chunk.channel)
+#define wave_header_get_bpsmpl(header) \
+ le_uint16((header)->format_chunk.blockalign)
+
+int ms_read_wav_header_from_fd(wave_header_t *header,int fd);
+
+#endif
diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h
index 887e2734a..eb1d21561 100644
--- a/tester/liblinphone_tester.h
+++ b/tester/liblinphone_tester.h
@@ -61,6 +61,7 @@ extern test_suite_t multicast_call_test_suite;
extern test_suite_t multi_call_test_suite;
extern test_suite_t proxy_config_test_suite;
extern test_suite_t vcard_test_suite;
+extern test_suite_t audio_bypass_suite;
#if HAVE_SIPP
extern test_suite_t complex_sip_call_test_suite;
#endif
diff --git a/tester/sounds/hello44100.wav b/tester/sounds/hello44100.wav
new file mode 100644
index 000000000..f478f856d
Binary files /dev/null and b/tester/sounds/hello44100.wav differ
diff --git a/tester/tester.c b/tester/tester.c
index f64402c23..e9973d52d 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -480,6 +480,7 @@ void liblinphone_tester_add_suites() {
bc_tester_add_suite(&tunnel_test_suite);
bc_tester_add_suite(&offeranswer_test_suite);
bc_tester_add_suite(&call_test_suite);
+ bc_tester_add_suite(&audio_bypass_suite);
bc_tester_add_suite(&multi_call_test_suite);
bc_tester_add_suite(&message_test_suite);
bc_tester_add_suite(&presence_test_suite);