mirror of https://github.com/FreeRDP/FreeRDP
channels/rdpsnd: initial attempt at adding GSM610 support
This commit is contained in:
parent
690a6b624d
commit
76c842285d
|
@ -381,6 +381,10 @@ set(JPEG_FEATURE_TYPE "OPTIONAL")
|
||||||
set(JPEG_FEATURE_PURPOSE "codec")
|
set(JPEG_FEATURE_PURPOSE "codec")
|
||||||
set(JPEG_FEATURE_DESCRIPTION "use JPEG library")
|
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)
|
if(WIN32)
|
||||||
set(X11_FEATURE_TYPE "DISABLED")
|
set(X11_FEATURE_TYPE "DISABLED")
|
||||||
set(ZLIB_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(Gstreamer ${GSTREAMER_FEATURE_TYPE} ${GSTREAMER_FEATURE_PURPOSE} ${GSTREAMER_FEATURE_DESCRIPTION})
|
||||||
|
|
||||||
find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_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(TARGET_ARCH MATCHES "x86|x64")
|
||||||
if (NOT APPLE)
|
if (NOT APPLE)
|
||||||
|
|
|
@ -32,7 +32,11 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||||
MODULE freerdp
|
MODULE freerdp
|
||||||
MODULES freerdp-codec freerdp-utils)
|
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})
|
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,15 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
#include <winpr/crt.h>
|
||||||
|
#include <winpr/stream.h>
|
||||||
#include <winpr/cmdline.h>
|
#include <winpr/cmdline.h>
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
#ifdef WITH_GSM
|
||||||
|
#include <gsm/gsm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <freerdp/types.h>
|
#include <freerdp/types.h>
|
||||||
#include <freerdp/codec/dsp.h>
|
#include <freerdp/codec/dsp.h>
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
#include <freerdp/utils/svc_plugin.h>
|
||||||
|
@ -52,6 +57,11 @@ struct rdpsnd_pulse_plugin
|
||||||
int latency;
|
int latency;
|
||||||
|
|
||||||
FREERDP_DSP_CONTEXT* dsp_context;
|
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)
|
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;
|
break;
|
||||||
|
|
||||||
case WAVE_FORMAT_GSM610:
|
case WAVE_FORMAT_GSM610:
|
||||||
|
sample_spec.format = PA_SAMPLE_S16LE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,6 +342,14 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
|
||||||
if (state == PA_STREAM_READY)
|
if (state == PA_STREAM_READY)
|
||||||
{
|
{
|
||||||
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
|
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");
|
DEBUG_SVC("connected");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -410,7 +429,18 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORM
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,63 +489,108 @@ static void rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
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)
|
static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||||
{
|
{
|
||||||
int len;
|
int length;
|
||||||
int ret;
|
int status;
|
||||||
BYTE* src;
|
BYTE* pcmData;
|
||||||
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
|
||||||
|
|
||||||
if (!pulse->stream)
|
if (!pulse->stream)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (pulse->format == WAVE_FORMAT_ADPCM)
|
pcmData = rdpsnd_pulse_convert_audio(device, data, &size);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pulse->mainloop);
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
|
|
||||||
while (size > 0)
|
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);
|
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 0)
|
if (length < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (len > size)
|
if (length > size)
|
||||||
len = 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)",
|
DEBUG_WARN("pa_stream_write failed (%d)",
|
||||||
pa_context_errno(pulse->context));
|
pa_context_errno(pulse->context));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
src += len;
|
pcmData += length;
|
||||||
size -= len;
|
size -= length;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(pulse->mainloop);
|
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();
|
pulse->dsp_context = freerdp_dsp_context_new();
|
||||||
|
|
||||||
|
#ifdef WITH_GSM
|
||||||
|
pulse->gsmBuffer = Stream_New(NULL, 4096);
|
||||||
|
#endif
|
||||||
|
|
||||||
pulse->mainloop = pa_threaded_mainloop_new();
|
pulse->mainloop = pa_threaded_mainloop_new();
|
||||||
|
|
||||||
if (!pulse->mainloop)
|
if (!pulse->mainloop)
|
||||||
|
|
|
@ -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)
|
|
@ -44,6 +44,7 @@
|
||||||
#cmakedefine WITH_PULSE
|
#cmakedefine WITH_PULSE
|
||||||
#cmakedefine WITH_IOSAUDIO
|
#cmakedefine WITH_IOSAUDIO
|
||||||
#cmakedefine WITH_OPENSLES
|
#cmakedefine WITH_OPENSLES
|
||||||
|
#cmakedefine WITH_GSM
|
||||||
|
|
||||||
/* Plugins */
|
/* Plugins */
|
||||||
#cmakedefine STATIC_CHANNELS
|
#cmakedefine STATIC_CHANNELS
|
||||||
|
|
|
@ -35,8 +35,36 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
|
||||||
* http://msdn.microsoft.com/en-us/library/ms713497.aspx
|
* http://msdn.microsoft.com/en-us/library/ms713497.aspx
|
||||||
*/
|
*/
|
||||||
|
|
||||||
wSamples = (size * 8) / format->wBitsPerSample;
|
if (format->wBitsPerSample)
|
||||||
mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels);
|
{
|
||||||
|
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;
|
return mstime;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue