diff --git a/mediastreamer2 b/mediastreamer2 index e998419b7..75fe8bcf6 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit e998419b7fd1e43a39974d2cf408ae169888a62b +Subproject commit 75fe8bcf69ecb6e08153242d5b5c2b7fa0c36e11 diff --git a/tester/Makefile.am b/tester/Makefile.am index d872f6917..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 \ diff --git a/tester/audio_bypass_tester.c b/tester/audio_bypass_tester.c index 70eba439b..8b29db138 100644 --- a/tester/audio_bypass_tester.c +++ b/tester/audio_bypass_tester.c @@ -19,29 +19,250 @@ #include "liblinphone_tester.h" #include "private.h" +/********************************************************************** + * This is a (simpler) copy of msfileplay filter in mediastreamer2 * + *********************************************************************/ + +#include "mediastreamer2/src/audiofilters/waveheader.h" + +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; +#ifdef HAVE_PCAP + pcap_t *pcap; + struct pcap_pkthdr *pcap_hdr; + const u_char *pcap_data; + bool_t pcap_started; + uint64_t pcap_initial_time; + uint32_t pcap_initial_ts; + uint16_t pcap_seq; +#endif +}; + +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;ib_wptr, 0, 10); - m->b_wptr += 10; - ms_queue_put(f->outputs[0], m); + PlayerData *d=(PlayerData*)f->data; + 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) { } +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; @@ -61,6 +282,8 @@ static int audio_bypass_snd_read_get_fmt(MSFilter *f, void *arg) { } 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 }, @@ -92,7 +315,8 @@ static void audio_bypass_snd_write_preprocess(MSFilter *f) { } static void audio_bypass_snd_write_process(MSFilter *f) { - ms_queue_get(f->inputs[0]); + mblk_t *m = ms_queue_get(f->inputs[0]); + ms_free(m); } static void audio_bypass_snd_write_postprocess(MSFilter *f) { @@ -103,6 +327,14 @@ 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; @@ -122,6 +354,8 @@ static int audio_bypass_snd_write_get_fmt(MSFilter *f, void *arg) { } 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 }, @@ -214,6 +448,10 @@ static void audio_bypass(void) { bool_t call_ok; MSList *marie_audio_codecs = marie_lc->codecs_conf.audio_codecs; MSList *pauline_audio_codecs = pauline_lc->codecs_conf.audio_codecs; + char *hellopath = bc_tester_res("sounds/hello44100.wav"); + char *recordpath = bc_tester_file("audiobypass-record.wav"); + double similar=1; + const double threshold = 0.85; // Enable L16 audio codec only_enable_payload(marie_audio_codecs, "L16", 1); @@ -222,16 +460,20 @@ static void audio_bypass(void) { // 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_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); @@ -239,12 +481,17 @@ static void audio_bypass(void) { 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, 10000); + 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: linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); + bc_free(recordpath); } test_t audio_bypass_tests[] = { 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