From 76c842285d21086c0b6d491af973da845690c3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 22 Nov 2013 12:11:39 -0500 Subject: [PATCH] channels/rdpsnd: initial attempt at adding GSM610 support --- CMakeLists.txt | 5 + channels/rdpsnd/client/pulse/CMakeLists.txt | 6 +- channels/rdpsnd/client/pulse/rdpsnd_pulse.c | 141 +++++++++++++++----- cmake/FindGSM.cmake | 13 ++ config.h.in | 1 + libfreerdp/codec/audio.c | 32 ++++- 6 files changed, 164 insertions(+), 34 deletions(-) create mode 100644 cmake/FindGSM.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e57790cba..c2316d5d2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,6 +381,10 @@ set(JPEG_FEATURE_TYPE "OPTIONAL") set(JPEG_FEATURE_PURPOSE "codec") set(JPEG_FEATURE_DESCRIPTION "use JPEG library") +set(GSM_FEATURE_TYPE "OPTIONAL") +set(GSM_FEATURE_PURPOSE "codec") +set(GSM_FEATURE_DESCRIPTION "GSM audio codec library") + if(WIN32) set(X11_FEATURE_TYPE "DISABLED") set(ZLIB_FEATURE_TYPE "DISABLED") @@ -445,6 +449,7 @@ find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FE find_feature(Gstreamer ${GSTREAMER_FEATURE_TYPE} ${GSTREAMER_FEATURE_PURPOSE} ${GSTREAMER_FEATURE_DESCRIPTION}) find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION}) +find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION}) if(TARGET_ARCH MATCHES "x86|x64") if (NOT APPLE) diff --git a/channels/rdpsnd/client/pulse/CMakeLists.txt b/channels/rdpsnd/client/pulse/CMakeLists.txt index d652ba234..6b6de1ecc 100644 --- a/channels/rdpsnd/client/pulse/CMakeLists.txt +++ b/channels/rdpsnd/client/pulse/CMakeLists.txt @@ -32,7 +32,11 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MODULE freerdp MODULES freerdp-codec freerdp-utils) -set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${PULSE_LIBRARY}) +list(APPEND ${MODULE_PREFIX}_LIBS ${PULSE_LIBRARY}) + +if(GSM_FOUND) + list(APPEND ${MODULE_PREFIX}_LIBS ${GSM_LIBRARIES}) +endif() target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c index cb0bea63d..e20cce9be 100644 --- a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c @@ -26,10 +26,15 @@ #include #include +#include #include #include +#ifdef WITH_GSM +#include +#endif + #include #include #include @@ -52,6 +57,11 @@ struct rdpsnd_pulse_plugin int latency; FREERDP_DSP_CONTEXT* dsp_context; + +#ifdef WITH_GSM + gsm gsm_context; + wStream* gsmBuffer; +#endif }; static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userdata) @@ -241,6 +251,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT* break; case WAVE_FORMAT_GSM610: + sample_spec.format = PA_SAMPLE_S16LE; break; } @@ -331,6 +342,14 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, if (state == PA_STREAM_READY) { freerdp_dsp_context_reset_adpcm(pulse->dsp_context); + +#ifdef WITH_GSM + if (pulse->gsm_context) + gsm_destroy(pulse->gsm_context); + + pulse->gsm_context = gsm_create(); +#endif + DEBUG_SVC("connected"); } else @@ -410,7 +429,18 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORM return TRUE; } break; + +#ifdef WITH_GSM + case WAVE_FORMAT_GSM610: + if ((format->nSamplesPerSec <= PA_RATE_MAX) && + (format->nBlockAlign == 65) && (format->nChannels == 1)) + { + return TRUE; + } + break; +#endif } + return FALSE; } @@ -459,63 +489,108 @@ static void rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value) pa_threaded_mainloop_unlock(pulse->mainloop); } +static BYTE* rdpsnd_pulse_convert_audio(rdpsndDevicePlugin* device, BYTE* data, int* size) +{ + BYTE* pcmData = NULL; + rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device; + + if (pulse->format == WAVE_FORMAT_ADPCM) + { + pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, + data, *size, pulse->sample_spec.channels, pulse->block_size); + + *size = pulse->dsp_context->adpcm_size; + pcmData = pulse->dsp_context->adpcm_buffer; + } + else if (pulse->format == WAVE_FORMAT_DVI_ADPCM) + { + pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, + data, *size, pulse->sample_spec.channels, pulse->block_size); + + *size = pulse->dsp_context->adpcm_size; + pcmData = pulse->dsp_context->adpcm_buffer; + } +#ifdef WITH_GSM + else if (pulse->format == WAVE_FORMAT_GSM610) + { + int inPos = 0; + int inSize = *size; + UINT16 gsmBlockBuffer[160]; + + Stream_SetPosition(pulse->gsmBuffer, 0); + + while (inSize) + { + ZeroMemory(gsmBlockBuffer, sizeof(gsmBlockBuffer)); + gsm_decode(pulse->gsm_context, (gsm_byte*) &data[inPos], (gsm_signal*) gsmBlockBuffer); + + if ((inPos % 65) == 0) + { + inPos += 33; + inSize -= 33; + } + else + { + inPos += 32; + inSize -= 32; + } + + Stream_EnsureRemainingCapacity(pulse->gsmBuffer, 160 * 2); + Stream_Write(pulse->gsmBuffer, (void*) gsmBlockBuffer, 160 * 2); + } + + Stream_SealLength(pulse->gsmBuffer); + + pcmData = Stream_Buffer(pulse->gsmBuffer); + *size = Stream_Length(pulse->gsmBuffer); + } +#endif + else + { + pcmData = data; + } + + return pcmData; +} + static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size) { - int len; - int ret; - BYTE* src; + int length; + int status; + BYTE* pcmData; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device; if (!pulse->stream) return; - if (pulse->format == WAVE_FORMAT_ADPCM) - { - pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, - data, size, pulse->sample_spec.channels, pulse->block_size); - - size = pulse->dsp_context->adpcm_size; - src = pulse->dsp_context->adpcm_buffer; - } - else if (pulse->format == WAVE_FORMAT_DVI_ADPCM) - { - pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, - data, size, pulse->sample_spec.channels, pulse->block_size); - - size = pulse->dsp_context->adpcm_size; - src = pulse->dsp_context->adpcm_buffer; - } - else - { - src = data; - } + pcmData = rdpsnd_pulse_convert_audio(device, data, &size); pa_threaded_mainloop_lock(pulse->mainloop); while (size > 0) { - while ((len = pa_stream_writable_size(pulse->stream)) == 0) + while ((length = pa_stream_writable_size(pulse->stream)) == 0) { pa_threaded_mainloop_wait(pulse->mainloop); } - if (len < 0) + if (length < 0) break; - if (len > size) - len = size; + if (length > size) + length = size; - ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE); + status = pa_stream_write(pulse->stream, pcmData, length, NULL, 0LL, PA_SEEK_RELATIVE); - if (ret < 0) + if (status < 0) { DEBUG_WARN("pa_stream_write failed (%d)", pa_context_errno(pulse->context)); break; } - src += len; - size -= len; + pcmData += length; + size -= length; } pa_threaded_mainloop_unlock(pulse->mainloop); @@ -595,6 +670,10 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE pulse->dsp_context = freerdp_dsp_context_new(); +#ifdef WITH_GSM + pulse->gsmBuffer = Stream_New(NULL, 4096); +#endif + pulse->mainloop = pa_threaded_mainloop_new(); if (!pulse->mainloop) diff --git a/cmake/FindGSM.cmake b/cmake/FindGSM.cmake new file mode 100644 index 000000000..366fd6764 --- /dev/null +++ b/cmake/FindGSM.cmake @@ -0,0 +1,13 @@ + +find_path(GSM_INCLUDE_DIR gsm/gsm.h) + +find_library(GSM_LIBRARY gsm) + +find_package_handle_standard_args(GSM DEFAULT_MSG GSM_INCLUDE_DIR GSM_LIBRARY) + +if(GSM_FOUND) + set(GSM_LIBRARIES ${GSM_LIBRARY}) + set(GSM_INCLUDE_DIRS ${GSM_INCLUDE_DIR}) +endif() + +mark_as_advanced(GSM_INCLUDE_DIR GSM_LIBRARY) diff --git a/config.h.in b/config.h.in index 043592ca6..aed1f6f09 100755 --- a/config.h.in +++ b/config.h.in @@ -44,6 +44,7 @@ #cmakedefine WITH_PULSE #cmakedefine WITH_IOSAUDIO #cmakedefine WITH_OPENSLES +#cmakedefine WITH_GSM /* Plugins */ #cmakedefine STATIC_CHANNELS diff --git a/libfreerdp/codec/audio.c b/libfreerdp/codec/audio.c index 1e45989d3..751885e78 100644 --- a/libfreerdp/codec/audio.c +++ b/libfreerdp/codec/audio.c @@ -35,8 +35,36 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size) * http://msdn.microsoft.com/en-us/library/ms713497.aspx */ - wSamples = (size * 8) / format->wBitsPerSample; - mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + if (format->wBitsPerSample) + { + wSamples = (size * 8) / format->wBitsPerSample; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } + else + { + mstime = 0; + + if (format->wFormatTag == WAVE_FORMAT_GSM610) + { + UINT16 nSamplesPerBlock; + + if ((format->cbSize == 2) && (format->data)) + { + nSamplesPerBlock = *((UINT16*) format->data); + + wSamples = (size / format->nBlockAlign) * nSamplesPerBlock; + mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); + } + else + { + fprintf(stderr, "rdpsnd_compute_audio_time_length: invalid WAVE_FORMAT_GSM610 format\n"); + } + } + else + { + fprintf(stderr, "rdpsnd_compute_audio_time_length: unknown format %d\n", format->wFormatTag); + } + } return mstime; }