From 2839f5d7553e01124712b8f7f4fd501dabad8ebc Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 23 Sep 2013 12:38:05 +0200 Subject: [PATCH] Renamed plugins from 'opensl_es' to 'opensles' --- channels/audin/client/CMakeLists.txt | 4 +- channels/audin/client/audin_main.c | 2 +- .../audin/client/opensl_es/audin_opensl_es.c | 419 ------------ .../{opensl_es => opensles}/CMakeLists.txt | 4 +- .../audin/client/opensles/audin_opensl_es.c | 420 ++++++++++++ .../{opensl_es => opensles}/opensl_io.c | 0 .../{opensl_es => opensles}/opensl_io.h | 0 channels/rdpsnd/client/CMakeLists.txt | 4 +- .../rdpsnd/client/opensles/CMakeLists.txt | 43 ++ channels/rdpsnd/client/opensles/opensl_io.c | 623 ++++++++++++++++++ channels/rdpsnd/client/opensles/opensl_io.h | 138 ++++ .../rdpsnd/client/opensles/rdpsnd_opensl_es.c | 382 +++++++++++ channels/rdpsnd/client/rdpsnd_main.c | 37 +- channels/rdpsnd/client/rdpsnd_main.h | 6 + 14 files changed, 1648 insertions(+), 434 deletions(-) delete mode 100644 channels/audin/client/opensl_es/audin_opensl_es.c rename channels/audin/client/{opensl_es => opensles}/CMakeLists.txt (91%) create mode 100644 channels/audin/client/opensles/audin_opensl_es.c rename channels/audin/client/{opensl_es => opensles}/opensl_io.c (100%) rename channels/audin/client/{opensl_es => opensles}/opensl_io.h (100%) create mode 100644 channels/rdpsnd/client/opensles/CMakeLists.txt create mode 100644 channels/rdpsnd/client/opensles/opensl_io.c create mode 100644 channels/rdpsnd/client/opensles/opensl_io.h create mode 100644 channels/rdpsnd/client/opensles/rdpsnd_opensl_es.c diff --git a/channels/audin/client/CMakeLists.txt b/channels/audin/client/CMakeLists.txt index bd8fe97db..0391ed5b6 100644 --- a/channels/audin/client/CMakeLists.txt +++ b/channels/audin/client/CMakeLists.txt @@ -48,6 +48,6 @@ if(WITH_PULSE) add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "") endif() -if(WITH_OPENSL_ES) - add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensl_es" "") +if(WITH_OPENSLES) + add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "") endif() diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c index a7e9d0b64..a62bbab3a 100644 --- a/channels/audin/client/audin_main.c +++ b/channels/audin/client/audin_main.c @@ -593,7 +593,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) if (!audin->device) { - audin_set_subsystem(audin, "opensl_es"); + audin_set_subsystem(audin, "opensles"); audin_set_device_name(audin, "default"); audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args); } diff --git a/channels/audin/client/opensl_es/audin_opensl_es.c b/channels/audin/client/opensl_es/audin_opensl_es.c deleted file mode 100644 index d2c3bf7e5..000000000 --- a/channels/audin/client/opensl_es/audin_opensl_es.c +++ /dev/null @@ -1,419 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Audio Input Redirection Virtual Channel - OpenSL ES implementation - * - * Copyright 2013 Armin Novak - * - * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "opensl_io.h" -#include "audin_main.h" - -typedef struct _AudinOpenSL_ESDevice -{ - IAudinDevice iface; - - char* device_name; - OPENSL_STREAM *stream; - - UINT32 frames_per_packet; - UINT32 target_rate; - UINT32 actual_rate; - UINT32 target_channels; - UINT32 actual_channels; - int bytes_per_channel; - int wformat; - int block_size; - - FREERDP_DSP_CONTEXT* dsp_context; - - HANDLE thread; - HANDLE stopEvent; - - void* user_data; -} AudinOpenSL_ESDevice; - -static BOOL audin_opensl_es_thread_receive(AudinOpenSL_ESDevice* opensl_es, - void* src, int count) -{ - int frames; - int cframes; - int ret = 0; - int encoded_size; - BYTE* encoded_data; - int rbytes_per_frame; - int tbytes_per_frame; - - rbytes_per_frame = opensl_es->actual_channels * opensl_es->bytes_per_channel; - tbytes_per_frame = opensl_es->target_channels * opensl_es->bytes_per_channel; - - if ((opensl_es->target_rate == opensl_es->actual_rate) && - (opensl_es->target_channels == opensl_es->actual_channels)) - { - frames = size / rbytes_per_frame; - } - else - { - opensl_es->dsp_context->resample(opensl_es->dsp_context, src, - opensl_es->bytes_per_channel, opensl_es->actual_channels, - opensl_es->actual_rate, size / rbytes_per_frame, - opensl_es->target_channels, opensl_es->target_rate); - frames = opensl_es->dsp_context->resampled_frames; - - DEBUG_DVC("resampled %d frames at %d to %d frames at %d", - size / rbytes_per_frame, opensl_es->actual_rate, - frames, opensl_es->target_rate); - - size = frames * tbytes_per_frame; - src = opensl_es->dsp_context->resampled_buffer; - } - - while (frames > 0) - { - if (WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0) - break; - - cframes = opensl_es->frames_per_packet - opensl_es->buffer_frames; - - if (cframes > frames) - cframes = frames; - - CopyMemory(opensl_es->buffer + opensl_es->buffer_frames * tbytes_per_frame, - src, cframes * tbytes_per_frame); - - opensl_es->buffer_frames += cframes; - - if (opensl_es->buffer_frames >= opensl_es->frames_per_packet) - { - if (opensl_es->wformat == WAVE_FORMAT_DVI_ADPCM) - { - opensl_es->dsp_context->encode_ima_adpcm(opensl_es->dsp_context, - opensl_es->buffer, opensl_es->buffer_frames * tbytes_per_frame, - opensl_es->target_channels, opensl_es->block_size); - - encoded_data = opensl_es->dsp_context->adpcm_buffer; - encoded_size = opensl_es->dsp_context->adpcm_size; - - DEBUG_DVC("encoded %d to %d", - opensl_es->buffer_frames * tbytes_per_frame, encoded_size); - } - else - { - encoded_data = opensl_es->buffer; - encoded_size = opensl_es->buffer_frames * tbytes_per_frame; - } - - if (WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0) - break; - else - ret = opensl_es->receive(encoded_data, encoded_size, - opensl_es->user_data); - - opensl_es->buffer_frames = 0; - - if (!ret) - break; - } - - src += cframes * tbytes_per_frame; - frames -= cframes; - } - - return (ret) ? TRUE : FALSE; -} - -static void* audin_opensl_es_thread_func(void* arg) -{ - float* buffer; - int rbytes_per_frame; - int tbytes_per_frame; - snd_pcm_t* capture_handle = NULL; - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) arg; - - DEBUG_DVC("opensl_es=%p", opensl_es); - - buffer = (BYTE*) calloc(sizeof(float), opensl_es->frames_per_packet); - ZeroMemory(buffer, opensl_es->frames_per_packet); - freerdp_dsp_context_reset_adpcm(opensl_es->dsp_context); - - while (!(WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0)) - { - int rc = android_AudioIn(opensl_es->stream, buffer, - opensl_es->frames_per_packet); - if (rc < 0) - { - DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error)); - break; - } - - if (!audin_opensl_es_thread_receive(opensl_es, buffer, rc * sizeof(float))) - break; - } - - free(buffer); - - DEBUG_DVC("thread shutdown."); - - ExitThread(0); - return NULL; -} - -static void audin_opensl_es_free(IAudinDevice* device) -{ - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - - DEBUG_DVC("device=%p", device); - - freerdp_dsp_context_free(opensl_es->dsp_context); - - free(opensl_es->device_name); - free(opensl_es); -} - -static BOOL audin_opensl_es_format_supported(IAudinDevice* device, audinFormat* format) -{ - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - SLResult rc; - - DEBUG_DVC("device=%p, format=%p", device, format); - - switch (format->wFormatTag) - { - case WAVE_FORMAT_PCM: - if (format->cbSize == 0 && - (format->nSamplesPerSec <= 48000) && - (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && - (format->nChannels == 1 || format->nChannels == 2)) - { - return TRUE; - } - break; - - case WAVE_FORMAT_DVI_ADPCM: - if ((format->nSamplesPerSec <= 48000) && - (format->wBitsPerSample == 4) && - (format->nChannels == 1 || format->nChannels == 2)) - { - return TRUE; - } - break; - } - - return FALSE; -} - -static void audin_opensl_es_set_format(IAudinDevice* device, - audinFormat* format, UINT32 FramesPerPacket) -{ - int bs; - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - - DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%d", - device, format, FramesPerPacket); - - opensl_es->target_rate = format->nSamplesPerSec; - opensl_es->actual_rate = format->nSamplesPerSec; - opensl_es->target_channels = format->nChannels; - opensl_es->actual_channels = format->nChannels; - - switch (format->wFormatTag) - { - case WAVE_FORMAT_PCM: - switch (format->wBitsPerSample) - { - case 8: - opensl_es->format = SND_PCM_FORMAT_S8; - opensl_es->bytes_per_channel = 1; - break; - case 16: - opensl_es->format = SND_PCM_FORMAT_S16_LE; - opensl_es->bytes_per_channel = 2; - break; - } - break; - - case WAVE_FORMAT_DVI_ADPCM: - opensl_es->format = SND_PCM_FORMAT_S16_LE; - opensl_es->bytes_per_channel = 2; - bs = (format->nBlockAlign - 4 * format->nChannels) * 4; - opensl_es->frames_per_packet = - (opensl_es->frames_per_packet * format->nChannels * 2 / - bs + 1) * bs / (format->nChannels * 2); - DEBUG_DVC("aligned FramesPerPacket=%d", - opensl_es->frames_per_packet); - break; - } - - opensl_es->wformat = format->wFormatTag; - opensl_es->block_size = format->nBlockAlign; -} - -static int audin_opensl_es_open(IAudinDevice* device, AudinReceive receive, - void* user_data) -{ - int status = 0; - int rbytes_per_frame; - int tbytes_per_frame; - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - - DEBUG_DVC("device=%p, receive=%d, user_data=%p", device, receive, user_data); - - opensl_es->stream = android_OpenAudioDevice(opensl_es->target_rate, - opensl_es->target_channels, 0, opensl_es->frames_per_packet); - - opensl_es->receive = receive; - opensl_es->user_data = user_data; - - rbytes_per_frame = opensl_es->actual_channels * opensl_es->bytes_per_channel; - tbytes_per_frame = opensl_es->target_channels * opensl_es->bytes_per_channel; - opensl_es->buffer = - (BYTE*) malloc(tbytes_per_frame * opensl_es->frames_per_packet); - ZeroMemory(opensl_es->buffer, - tbytes_per_frame * opensl_es->frames_per_packet); - opensl_es->buffer_frames = 0; - - opensl_es->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - opensl_es->thread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) audin_opensl_es_thread_func, - opensl_es, 0, NULL); -} - -static void audin_opensl_es_close(IAudinDevice* device) -{ - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - - DEBUG_DVC("device=%p", device); - - SetEvent(opensl_es->stopEvent); - WaitForSingleObject(opensl_es->thread, INFINITE); - CloseHandle(opensl_es->stopEvent); - CloseHandle(opensl_es->thread); - - android_CloseAudioDevice(opensl_es->stream); - - opensl_es->stopEvent = NULL; - opensl_es->thread = NULL; - opensl_es->receive = NULL; - opensl_es->user_data = NULL; - opsnsl_es->stream = NULL; -} - -static const COMMAND_LINE_ARGUMENT_A audin_opensl_es_args[] = -{ - { "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "", - NULL, NULL, -1, NULL, "audio device name" }, - { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } -}; - -static int audin_opensl_es_parse_addin_args(AudinOpenSL_ESDevice* device, - ADDIN_ARGV* args) -{ - int status; - DWORD flags; - COMMAND_LINE_ARGUMENT_A* arg; - AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device; - - DEBUG_DVC("device=%p, args=%p", device, args); - - flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON; - - status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, - audin_opensl_es_args, flags, opensl_es, NULL, NULL); - if (status < 0) - return status; - - arg = audin_opensl_es_args; - - do - { - if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT)) - continue; - - CommandLineSwitchStart(arg) - - CommandLineSwitchCase(arg, "audio-dev") - { - opensl_es->device_name = _strdup(arg->Value); - } - - CommandLineSwitchEnd(arg) - } - while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); - - return status; -} - -#ifdef STATIC_CHANNELS -#define freerdp_audin_client_subsystem_entry \ - opensl_es_freerdp_audin_client_subsystem_entry -#endif - -int freerdp_audin_client_subsystem_entry( - PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints) -{ - ADDIN_ARGV* args; - AudinOpenSL_ESDevice* opensl_es; - - DEBUG_DVC("pEntryPoints=%p", pEntryPoints); - - opensl_es = (AudinOpenSL_ESDevice*) malloc(sizeof(AudinOpenSL_ESDevice)); - ZeroMemory(opensl_es, sizeof(AudinOpenSL_ESDevice)); - - opensl_es->iface.Open = audin_opensl_es_open; - opensl_es->iface.FormatSupported = audin_opensl_es_format_supported; - opensl_es->iface.SetFormat = audin_opensl_es_set_format; - opensl_es->iface.Close = audin_opensl_es_close; - opensl_es->iface.Free = audin_opensl_es_free; - - args = pEntryPoints->args; - - audin_opensl_es_parse_addin_args(opensl_es, args); - - if (!opensl_es->device_name) - opensl_es->device_name = _strdup("default"); - - opensl_es->frames_per_packet = 128; - opensl_es->target_rate = 22050; - opensl_es->actual_rate = 22050; - opensl_es->format = SND_PCM_FORMAT_S16_LE; - opensl_es->target_channels = 2; - opensl_es->actual_channels = 2; - opensl_es->bytes_per_channel = 2; - - opensl_es->dsp_context = freerdp_dsp_context_new(); - - pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, - (IAudinDevice*) opensl_es); - - return 0; -} diff --git a/channels/audin/client/opensl_es/CMakeLists.txt b/channels/audin/client/opensles/CMakeLists.txt similarity index 91% rename from channels/audin/client/opensl_es/CMakeLists.txt rename to channels/audin/client/opensles/CMakeLists.txt index 0c81befe6..45626cf6f 100644 --- a/channels/audin/client/opensl_es/CMakeLists.txt +++ b/channels/audin/client/opensles/CMakeLists.txt @@ -21,7 +21,7 @@ set(${MODULE_PREFIX}_SRCS audin_opensl_es.c) include_directories(..) -include_directories(${OPENSL_ES_INCLUDE_DIRS}) +include_directories(${OPENSLES_INCLUDE_DIRS}) add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") @@ -32,7 +32,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MODULE freerdp MODULES freerdp-codec freerdp-utils) -set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSL_ES_LIBRARIES}) +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSLES_LIBRARIES}) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/channels/audin/client/opensles/audin_opensl_es.c b/channels/audin/client/opensles/audin_opensl_es.c new file mode 100644 index 000000000..6da522fbc --- /dev/null +++ b/channels/audin/client/opensles/audin_opensl_es.c @@ -0,0 +1,420 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Audio Input Redirection Virtual Channel - OpenSL ES implementation + * + * Copyright 2013 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "opensl_io.h" +#include "audin_main.h" + +typedef struct _AudinOpenSLESDevice +{ + IAudinDevice iface; + + char* device_name; + OPENSL_STREAM *stream; + + UINT32 frames_per_packet; + UINT32 target_rate; + UINT32 actual_rate; + UINT32 target_channels; + UINT32 actual_channels; + int bytes_per_channel; + int wformat; + int format; + int block_size; + + FREERDP_DSP_CONTEXT* dsp_context; + + HANDLE thread; + HANDLE stopEvent; + + void* user_data; +} AudinOpenSLESDevice; + +static BOOL audin_opensles_thread_receive(AudinOpenSLESDevice* opensles, + void* src, int count) +{ + int frames; + int cframes; + int ret = 0; + int encoded_size; + BYTE* encoded_data; + int rbytes_per_frame; + int tbytes_per_frame; + + rbytes_per_frame = opensles->actual_channels * opensles->bytes_per_channel; + tbytes_per_frame = opensles->target_channels * opensles->bytes_per_channel; + + if ((opensles->target_rate == opensles->actual_rate) && + (opensles->target_channels == opensles->actual_channels)) + { + frames = size / rbytes_per_frame; + } + else + { + opensles->dsp_context->resample(opensles->dsp_context, src, + opensles->bytes_per_channel, opensles->actual_channels, + opensles->actual_rate, size / rbytes_per_frame, + opensles->target_channels, opensles->target_rate); + frames = opensles->dsp_context->resampled_frames; + + DEBUG_DVC("resampled %d frames at %d to %d frames at %d", + size / rbytes_per_frame, opensles->actual_rate, + frames, opensles->target_rate); + + size = frames * tbytes_per_frame; + src = opensles->dsp_context->resampled_buffer; + } + + while (frames > 0) + { + if (WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0) + break; + + cframes = opensles->frames_per_packet - opensles->buffer_frames; + + if (cframes > frames) + cframes = frames; + + CopyMemory(opensles->buffer + opensles->buffer_frames * tbytes_per_frame, + src, cframes * tbytes_per_frame); + + opensles->buffer_frames += cframes; + + if (opensles->buffer_frames >= opensles->frames_per_packet) + { + if (opensles->wformat == WAVE_FORMAT_DVI_ADPCM) + { + opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context, + opensles->buffer, opensles->buffer_frames * tbytes_per_frame, + opensles->target_channels, opensles->block_size); + + encoded_data = opensles->dsp_context->adpcm_buffer; + encoded_size = opensles->dsp_context->adpcm_size; + + DEBUG_DVC("encoded %d to %d", + opensles->buffer_frames * tbytes_per_frame, encoded_size); + } + else + { + encoded_data = opensles->buffer; + encoded_size = opensles->buffer_frames * tbytes_per_frame; + } + + if (WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0) + break; + else + ret = opensles->receive(encoded_data, encoded_size, + opensles->user_data); + + opensles->buffer_frames = 0; + + if (!ret) + break; + } + + src += cframes * tbytes_per_frame; + frames -= cframes; + } + + return (ret) ? TRUE : FALSE; +} + +static void* audin_opensles_thread_func(void* arg) +{ + float* buffer; + int rbytes_per_frame; + int tbytes_per_frame; + snd_pcm_t* capture_handle = NULL; + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg; + + DEBUG_SND("opensles=%p", opensles); + + buffer = (BYTE*) calloc(sizeof(float), opensles->frames_per_packet); + ZeroMemory(buffer, opensles->frames_per_packet); + freerdp_dsp_context_reset_adpcm(opensles->dsp_context); + + while (!(WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0)) + { + int rc = android_AudioIn(opensles->stream, buffer, + opensles->frames_per_packet); + if (rc < 0) + { + DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error)); + break; + } + + if (!audin_opensles_thread_receive(opensles, buffer, rc * sizeof(float))) + break; + } + + free(buffer); + + DEBUG_DVC("thread shutdown."); + + ExitThread(0); + return NULL; +} + +static void audin_opensles_free(IAudinDevice* device) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p", device); + + freerdp_dsp_context_free(opensles->dsp_context); + + free(opensles->device_name); + free(opensles); +} + +static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + SLResult rc; + + DEBUG_DVC("device=%p, format=%p", device, format); + + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + if (format->cbSize == 0 && + (format->nSamplesPerSec <= 48000) && + (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && + (format->nChannels == 1 || format->nChannels == 2)) + { + return TRUE; + } + break; + + case WAVE_FORMAT_DVI_ADPCM: + if ((format->nSamplesPerSec <= 48000) && + (format->wBitsPerSample == 4) && + (format->nChannels == 1 || format->nChannels == 2)) + { + return TRUE; + } + break; + } + + return FALSE; +} + +static void audin_opensles_set_format(IAudinDevice* device, + audinFormat* format, UINT32 FramesPerPacket) +{ + int bs; + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%d", + device, format, FramesPerPacket); + + opensles->target_rate = format->nSamplesPerSec; + opensles->actual_rate = format->nSamplesPerSec; + opensles->target_channels = format->nChannels; + opensles->actual_channels = format->nChannels; + + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + switch (format->wBitsPerSample) + { + case 8: + opensles->format = SND_PCM_FORMAT_S8; + opensles->bytes_per_channel = 1; + break; + case 16: + opensles->format = SND_PCM_FORMAT_S16_LE; + opensles->bytes_per_channel = 2; + break; + } + break; + + case WAVE_FORMAT_DVI_ADPCM: + opensles->format = SND_PCM_FORMAT_S16_LE; + opensles->bytes_per_channel = 2; + bs = (format->nBlockAlign - 4 * format->nChannels) * 4; + opensles->frames_per_packet = + (opensles->frames_per_packet * format->nChannels * 2 / + bs + 1) * bs / (format->nChannels * 2); + DEBUG_DVC("aligned FramesPerPacket=%d", + opensles->frames_per_packet); + break; + } + + opensles->wformat = format->wFormatTag; + opensles->block_size = format->nBlockAlign; +} + +static int audin_opensles_open(IAudinDevice* device, AudinReceive receive, + void* user_data) +{ + int status = 0; + int rbytes_per_frame; + int tbytes_per_frame; + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p, receive=%d, user_data=%p", device, receive, user_data); + + opensles->stream = android_OpenAudioDevice(opensles->target_rate, + opensles->target_channels, 0, opensles->frames_per_packet); + + opensles->receive = receive; + opensles->user_data = user_data; + + rbytes_per_frame = opensles->actual_channels * opensles->bytes_per_channel; + tbytes_per_frame = opensles->target_channels * opensles->bytes_per_channel; + opensles->buffer = + (BYTE*) malloc(tbytes_per_frame * opensles->frames_per_packet); + ZeroMemory(opensles->buffer, + tbytes_per_frame * opensles->frames_per_packet); + opensles->buffer_frames = 0; + + opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + opensles->thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) audin_opensles_thread_func, + opensles, 0, NULL); +} + +static void audin_opensles_close(IAudinDevice* device) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p", device); + + SetEvent(opensles->stopEvent); + WaitForSingleObject(opensles->thread, INFINITE); + CloseHandle(opensles->stopEvent); + CloseHandle(opensles->thread); + + android_CloseAudioDevice(opensles->stream); + + opensles->stopEvent = NULL; + opensles->thread = NULL; + opensles->receive = NULL; + opensles->user_data = NULL; + opsnsles->stream = NULL; +} + +static const COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = +{ + { "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "", + NULL, NULL, -1, NULL, "audio device name" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } +}; + +static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, + ADDIN_ARGV* args) +{ + int status; + DWORD flags; + COMMAND_LINE_ARGUMENT_A* arg; + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p, args=%p", device, args); + + flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON; + + status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, + audin_opensles_args, flags, opensles, NULL, NULL); + if (status < 0) + return status; + + arg = audin_opensles_args; + + do + { + if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT)) + continue; + + CommandLineSwitchStart(arg) + + CommandLineSwitchCase(arg, "audio-dev") + { + opensles->device_name = _strdup(arg->Value); + } + + CommandLineSwitchEnd(arg) + } + while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return status; +} + +#ifdef STATIC_CHANNELS +#define freerdp_audin_client_subsystem_entry \ + opensles_freerdp_audin_client_subsystem_entry +#endif + +int freerdp_audin_client_subsystem_entry( + PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints) +{ + ADDIN_ARGV* args; + AudinOpenSLESDevice* opensles; + + DEBUG_DVC("pEntryPoints=%p", pEntryPoints); + + opensles = (AudinOpenSLESDevice*) malloc(sizeof(AudinOpenSLESDevice)); + ZeroMemory(opensles, sizeof(AudinOpenSLESDevice)); + + opensles->iface.Open = audin_opensles_open; + opensles->iface.FormatSupported = audin_opensles_format_supported; + opensles->iface.SetFormat = audin_opensles_set_format; + opensles->iface.Close = audin_opensles_close; + opensles->iface.Free = audin_opensles_free; + + args = pEntryPoints->args; + + audin_opensles_parse_addin_args(opensles, args); + + if (!opensles->device_name) + opensles->device_name = _strdup("default"); + + opensles->frames_per_packet = 128; + opensles->target_rate = 22050; + opensles->actual_rate = 22050; + opensles->format = SND_PCM_FORMAT_S16_LE; + opensles->target_channels = 2; + opensles->actual_channels = 2; + opensles->bytes_per_channel = 2; + + opensles->dsp_context = freerdp_dsp_context_new(); + + pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, + (IAudinDevice*) opensles); + + return 0; +} diff --git a/channels/audin/client/opensl_es/opensl_io.c b/channels/audin/client/opensles/opensl_io.c similarity index 100% rename from channels/audin/client/opensl_es/opensl_io.c rename to channels/audin/client/opensles/opensl_io.c diff --git a/channels/audin/client/opensl_es/opensl_io.h b/channels/audin/client/opensles/opensl_io.h similarity index 100% rename from channels/audin/client/opensl_es/opensl_io.h rename to channels/audin/client/opensles/opensl_io.h diff --git a/channels/rdpsnd/client/CMakeLists.txt b/channels/rdpsnd/client/CMakeLists.txt index 6d0c32806..8ec9bb336 100644 --- a/channels/rdpsnd/client/CMakeLists.txt +++ b/channels/rdpsnd/client/CMakeLists.txt @@ -59,6 +59,6 @@ if(WITH_WINMM) add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "") endif() -if(WITH_AUDIOTRACK) - add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "audiotrack" "") +if(WITH_OPENSLES) + add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "") endif() diff --git a/channels/rdpsnd/client/opensles/CMakeLists.txt b/channels/rdpsnd/client/opensles/CMakeLists.txt new file mode 100644 index 000000000..0e5660d6a --- /dev/null +++ b/channels/rdpsnd/client/opensles/CMakeLists.txt @@ -0,0 +1,43 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2013 Armin Novak +# +# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_client_subsystem("rdpsnd" "opensles" "") + +set(${MODULE_PREFIX}_SRCS + opensl_io.c + rdpsnd_opensles.c) + +include_directories(..) +include_directories(${OPENSLES_INCLUDE_DIRS}) + +add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") + +set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE freerdp + MODULES freerdp-codec freerdp-utils + ${OPENSLES_LIBRARIES}) + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSLES_LIBRARIES}) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +if(NOT STATIC_CHANNELS) + install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH}) +endif() diff --git a/channels/rdpsnd/client/opensles/opensl_io.c b/channels/rdpsnd/client/opensles/opensl_io.c new file mode 100644 index 000000000..d93aaae85 --- /dev/null +++ b/channels/rdpsnd/client/opensles/opensl_io.c @@ -0,0 +1,623 @@ +/* +opensl_io.c: +Android OpenSL input/output module +Copyright (c) 2012, Victor Lazzarini +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "rdpsnd_main.h" +#include "opensl_io.h" +#define CONV16BIT 32768 +#define CONVMYFLT (1./32768.) + +static void* createThreadLock(void); +static int waitThreadLock(void *lock); +static void notifyThreadLock(void *lock); +static void destroyThreadLock(void *lock); +static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context); +static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context); + +// creates the OpenSL ES audio engine +static SLresult openSLCreateEngine(OPENSL_STREAM *p) +{ + SLresult result; + // create engine + result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL); + DEBUG_SND("engineObject=%p", p->engineObject); + if(result != SL_RESULT_SUCCESS) goto engine_end; + + // realize the engine + result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE); + DEBUG_SND("Realize=%d", result); + if(result != SL_RESULT_SUCCESS) goto engine_end; + + // get the engine interface, which is needed in order to create other objects + result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine)); + DEBUG_SND("engineEngine=%p", p->engineEngine); + if(result != SL_RESULT_SUCCESS) goto engine_end; + + engine_end: + return result; +} + +// opens the OpenSL ES device for output +static SLresult openSLPlayOpen(OPENSL_STREAM *p) +{ + SLresult result; + SLuint32 sr = p->sr; + SLuint32 channels = p->outchannels; + + if(channels){ + // configure audio source + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + + switch(sr){ + + case 8000: + sr = SL_SAMPLINGRATE_8; + break; + case 11025: + sr = SL_SAMPLINGRATE_11_025; + break; + case 16000: + sr = SL_SAMPLINGRATE_16; + break; + case 22050: + sr = SL_SAMPLINGRATE_22_05; + break; + case 24000: + sr = SL_SAMPLINGRATE_24; + break; + case 32000: + sr = SL_SAMPLINGRATE_32; + break; + case 44100: + sr = SL_SAMPLINGRATE_44_1; + break; + case 48000: + sr = SL_SAMPLINGRATE_48; + break; + case 64000: + sr = SL_SAMPLINGRATE_64; + break; + case 88200: + sr = SL_SAMPLINGRATE_88_2; + break; + case 96000: + sr = SL_SAMPLINGRATE_96; + break; + case 192000: + sr = SL_SAMPLINGRATE_192; + break; + default: + return -1; + } + + const SLInterfaceID ids[] = {SL_IID_VOLUME}; + const SLboolean req[] = {SL_BOOLEAN_FALSE}; + result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, &(p->outputMixObject), 1, ids, req); + DEBUG_SND("engineEngine=%p", p->engineEngine); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // realize the output mix + result = (*p->outputMixObject)->Realize(p->outputMixObject, SL_BOOLEAN_FALSE); + DEBUG_SND("Realize=%d", result); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + int speakers; + if(channels > 1) + speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + else speakers = SL_SPEAKER_FRONT_CENTER; + SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,channels, sr, + SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, + speakers, SL_BYTEORDER_LITTLEENDIAN}; + + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + // configure audio sink + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, p->outputMixObject}; + SLDataSink audioSnk = {&loc_outmix, NULL}; + + // create audio player + const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME}; + const SLboolean req1[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine, + &(p->bqPlayerObject), &audioSrc, &audioSnk, 2, ids1, req1); + DEBUG_SND("bqPlayerObject=%p", p->bqPlayerObject); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // realize the player + result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, SL_BOOLEAN_FALSE); + DEBUG_SND("Realize=%d", result); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // get the play interface + result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAY, &(p->bqPlayerPlay)); + DEBUG_SND("bqPlayerPlay=%p", p->bqPlayerPlay); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // get the volume interface + result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_VOLUME, &(p->bqPlayerVolume)); + DEBUG_SND("bqPlayerVolume=%p", p->bqPlayerVolume); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // get the buffer queue interface + result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &(p->bqPlayerBufferQueue)); + DEBUG_SND("bqPlayerBufferQueue=%p", p->bqPlayerBufferQueue); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // register callback on the buffer queue + result = (*p->bqPlayerBufferQueue)->RegisterCallback(p->bqPlayerBufferQueue, bqPlayerCallback, p); + DEBUG_SND("bqPlayerCallback=%p", p->bqPlayerCallback); + assert(!result); + if(result != SL_RESULT_SUCCESS) goto end_openaudio; + + // set the player's state to playing + result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, SL_PLAYSTATE_PLAYING); + DEBUG_SND("SetPlayState=%d", result); + assert(!result); + + end_openaudio: + assert(!result); + return result; + } + return SL_RESULT_SUCCESS; +} + +// Open the OpenSL ES device for input +static SLresult openSLRecOpen(OPENSL_STREAM *p){ + + SLresult result; + SLuint32 sr = p->sr; + SLuint32 channels = p->inchannels; + + if(channels){ + + switch(sr){ + + case 8000: + sr = SL_SAMPLINGRATE_8; + break; + case 11025: + sr = SL_SAMPLINGRATE_11_025; + break; + case 16000: + sr = SL_SAMPLINGRATE_16; + break; + case 22050: + sr = SL_SAMPLINGRATE_22_05; + break; + case 24000: + sr = SL_SAMPLINGRATE_24; + break; + case 32000: + sr = SL_SAMPLINGRATE_32; + break; + case 44100: + sr = SL_SAMPLINGRATE_44_1; + break; + case 48000: + sr = SL_SAMPLINGRATE_48; + break; + case 64000: + sr = SL_SAMPLINGRATE_64; + break; + case 88200: + sr = SL_SAMPLINGRATE_88_2; + break; + case 96000: + sr = SL_SAMPLINGRATE_96; + break; + case 192000: + sr = SL_SAMPLINGRATE_192; + break; + default: + return -1; + } + + // configure audio source + SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; + SLDataSource audioSrc = {&loc_dev, NULL}; + + // configure audio sink + int speakers; + if(channels > 1) + speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + else speakers = SL_SPEAKER_FRONT_CENTER; + SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sr, + SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16, + speakers, SL_BYTEORDER_LITTLEENDIAN}; + SLDataSink audioSnk = {&loc_bq, &format_pcm}; + + // create audio recorder + // (requires the RECORD_AUDIO permission) + const SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME}; + const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine, + &(p->recorderObject), &audioSrc, &audioSnk, 2, id, req); + DEBUG_SND("p->recorderObject=%p", p->recorderObject); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + + // realize the audio recorder + result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE); + DEBUG_SND("Realize=%d", result); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + + // get the record interface + result = (*p->recorderObject)->GetInterface(p->recorderObject, + SL_IID_RECORD, &(p->recorderRecord)); + DEBUG_SND("p->recorderRecord=%p", p->recorderRecord); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + + // get the buffer queue interface + result = (*p->recorderObject)->GetInterface(p->recorderObject, + SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &(p->recorderBufferQueue)); + DEBUG_SND("p->recorderBufferQueue=%p", p->recorderBufferQueue); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + + // get the record volume + result = (*p->recorderObject)->GetInterface(p->recorderObject, + SL_IID_VOLUME, &(p->recorderVolume)); + DEBUG_SND("p->recorderVolume=%p", p->recorderVolume); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + + // register callback on the buffer queue + result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue, + bqRecorderCallback, p); + DEBUG_SND("p->recorderBufferQueue=%p", p->recorderBufferQueue); + assert(!result); + if (SL_RESULT_SUCCESS != result) goto end_recopen; + result = (*p->recorderRecord)->SetRecordState(p->recorderRecord, + SL_RECORDSTATE_RECORDING); + + end_recopen: + return result; + } + else return SL_RESULT_SUCCESS; + + +} + +// close the OpenSL IO and destroy the audio engine +static void openSLDestroyEngine(OPENSL_STREAM *p){ + + // destroy buffer queue audio player object, and invalidate all associated interfaces + if (p->bqPlayerObject != NULL) { + (*p->bqPlayerObject)->Destroy(p->bqPlayerObject); + p->bqPlayerObject = NULL; + p->bqPlayerVolume = NULL; + p->bqPlayerPlay = NULL; + p->bqPlayerBufferQueue = NULL; + p->bqPlayerEffectSend = NULL; + } + + // destroy audio recorder object, and invalidate all associated interfaces + if (p->recorderObject != NULL) { + (*p->recorderObject)->Destroy(p->recorderObject); + p->recorderObject = NULL; + p->recorderRecord = NULL; + p->recorderVolume = NULL; + p->recorderBufferQueue = NULL; + } + + // destroy output mix object, and invalidate all associated interfaces + if (p->outputMixObject != NULL) { + (*p->outputMixObject)->Destroy(p->outputMixObject); + p->outputMixObject = NULL; + } + + // destroy engine object, and invalidate all associated interfaces + if (p->engineObject != NULL) { + (*p->engineObject)->Destroy(p->engineObject); + p->engineObject = NULL; + p->engineEngine = NULL; + } + +} + + +// open the android audio device for input and/or output +OPENSL_STREAM *android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes){ + + OPENSL_STREAM *p; + p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1); + + p->inchannels = inchannels; + p->outchannels = outchannels; + p->sr = sr; + p->inlock = createThreadLock(); + p->outlock = createThreadLock(); + + if((p->outBufSamples = bufferframes*outchannels) != 0) { + if((p->outputBuffer[0] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL || + (p->outputBuffer[1] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL) { + android_CloseAudioDevice(p); + return NULL; + } + } + + if((p->inBufSamples = bufferframes*inchannels) != 0){ + if((p->inputBuffer[0] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL || + (p->inputBuffer[1] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL){ + android_CloseAudioDevice(p); + return NULL; + } + } + + p->currentInputIndex = 0; + p->currentOutputBuffer = 0; + p->currentInputIndex = p->inBufSamples; + p->currentInputBuffer = 0; + + if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) { + android_CloseAudioDevice(p); + return NULL; + } + + if(openSLRecOpen(p) != SL_RESULT_SUCCESS) { + android_CloseAudioDevice(p); + return NULL; + } + + if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) { + android_CloseAudioDevice(p); + return NULL; + } + + notifyThreadLock(p->outlock); + notifyThreadLock(p->inlock); + + p->time = 0.; + return p; +} + +// close the android audio device +void android_CloseAudioDevice(OPENSL_STREAM *p){ + + if (p == NULL) + return; + + openSLDestroyEngine(p); + + if (p->inlock != NULL) { + notifyThreadLock(p->inlock); + destroyThreadLock(p->inlock); + p->inlock = NULL; + } + + if (p->outlock != NULL) { + notifyThreadLock(p->outlock); + destroyThreadLock(p->outlock); + p->inlock = NULL; + } + + if (p->outputBuffer[0] != NULL) { + free(p->outputBuffer[0]); + p->outputBuffer[0] = NULL; + } + + if (p->outputBuffer[1] != NULL) { + free(p->outputBuffer[1]); + p->outputBuffer[1] = NULL; + } + + if (p->inputBuffer[0] != NULL) { + free(p->inputBuffer[0]); + p->inputBuffer[0] = NULL; + } + + if (p->inputBuffer[1] != NULL) { + free(p->inputBuffer[1]); + p->inputBuffer[1] = NULL; + } + + free(p); +} + +// returns timestamp of the processed stream +double android_GetTimestamp(OPENSL_STREAM *p){ + return p->time; +} + + +// this callback handler is called every time a buffer finishes recording +void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + OPENSL_STREAM *p = (OPENSL_STREAM *) context; + notifyThreadLock(p->inlock); +} + +// gets a buffer of size samples from the device +int android_AudioIn(OPENSL_STREAM *p,float *buffer,int size){ + short *inBuffer; + int i, bufsamps = p->inBufSamples, index = p->currentInputIndex; + if(p == NULL || bufsamps == 0) return 0; + + inBuffer = p->inputBuffer[p->currentInputBuffer]; + for(i=0; i < size; i++){ + if (index >= bufsamps) { + waitThreadLock(p->inlock); + (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, + inBuffer,bufsamps*sizeof(short)); + p->currentInputBuffer = (p->currentInputBuffer ? 0 : 1); + index = 0; + inBuffer = p->inputBuffer[p->currentInputBuffer]; + } + buffer[i] = (float) inBuffer[index++]*CONVMYFLT; + } + p->currentInputIndex = index; + if(p->outchannels == 0) p->time += (double) size/(p->sr*p->inchannels); + return i; +} + +// this callback handler is called every time a buffer finishes playing +void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + OPENSL_STREAM *p = (OPENSL_STREAM *) context; + notifyThreadLock(p->outlock); +} + +// puts a buffer of size samples to the device +int android_AudioOut(OPENSL_STREAM *p, short *buffer,int size){ + + short *outBuffer; + int i, bufsamps = p->outBufSamples, index = p->currentOutputIndex; + + assert(p); + assert(buffer); + assert(size > 0); + + if(p == NULL || bufsamps == 0) + return 0; + outBuffer = p->outputBuffer[p->currentOutputBuffer]; + + for(i=0; i < size; i++){ + outBuffer[index++] = buffer[i]; + if (index >= p->outBufSamples) { + waitThreadLock(p->outlock); + (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, + outBuffer,bufsamps*sizeof(short)); + p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1); + index = 0; + outBuffer = p->outputBuffer[p->currentOutputBuffer]; + } + } + p->currentOutputIndex = index; + p->time += (double) size/(p->sr*p->outchannels); + return i; +} + +int android_GetInputVolume(OPENSL_STREAM *p){ + SLmillibel level; + + assert(p); + assert(p->recorderVolume); + + SLresult rc = (*p->recorderVolume)->GetVolumeLevel(p->recorderVolume, &level); + assert(SL_RESULT_SUCCESS == rc); + + return level; +} + +void android_SetInputVolume(OPENSL_STREAM *p, int level){ + SLresult rc = (*p->recorderVolume)->SetVolumeLevel(p->recorderVolume, level); + assert(SL_RESULT_SUCCESS == rc); +} + +int android_GetOutputVolume(OPENSL_STREAM *p){ + SLmillibel level; + + assert(p); + assert(p->bqPlayerVolume); + + SLresult rc = (*p->bqPlayerVolume)->GetVolumeLevel(p->bqPlayerVolume, &level); + assert(SL_RESULT_SUCCESS == rc); + + return level; +} + +void android_SetOutputVolume(OPENSL_STREAM *p, int level){ + SLresult rc = (*p->bqPlayerVolume)->SetVolumeLevel(p->bqPlayerVolume, level); + assert(SL_RESULT_SUCCESS == rc); +} + +//---------------------------------------------------------------------- +// thread Locks +// to ensure synchronisation between callbacks and processing code +void* createThreadLock(void) +{ + threadLock *p; + p = (threadLock*) malloc(sizeof(threadLock)); + if (p == NULL) + return NULL; + memset(p, 0, sizeof(threadLock)); + if (pthread_mutex_init(&(p->m), (pthread_mutexattr_t*) NULL) != 0) { + free((void*) p); + return NULL; + } + if (pthread_cond_init(&(p->c), (pthread_condattr_t*) NULL) != 0) { + pthread_mutex_destroy(&(p->m)); + free((void*) p); + return NULL; + } + p->s = (unsigned char) 1; + + return p; +} + +int waitThreadLock(void *lock) +{ + threadLock *p; + int retval = 0; + p = (threadLock*) lock; + pthread_mutex_lock(&(p->m)); + while (!p->s) { + pthread_cond_wait(&(p->c), &(p->m)); + } + p->s = (unsigned char) 0; + pthread_mutex_unlock(&(p->m)); + + return retval; +} + +void notifyThreadLock(void *lock) +{ + threadLock *p; + p = (threadLock*) lock; + pthread_mutex_lock(&(p->m)); + p->s = (unsigned char) 1; + pthread_cond_signal(&(p->c)); + pthread_mutex_unlock(&(p->m)); +} + +void destroyThreadLock(void *lock) +{ + threadLock *p; + p = (threadLock*) lock; + if (p == NULL) + return; + notifyThreadLock(p); + pthread_cond_destroy(&(p->c)); + pthread_mutex_destroy(&(p->m)); + free(p); +} diff --git a/channels/rdpsnd/client/opensles/opensl_io.h b/channels/rdpsnd/client/opensles/opensl_io.h new file mode 100644 index 000000000..42c1d9d48 --- /dev/null +++ b/channels/rdpsnd/client/opensles/opensl_io.h @@ -0,0 +1,138 @@ +/* +opensl_io.c: +Android OpenSL input/output module header +Copyright (c) 2012, Victor Lazzarini +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef OPENSL_IO +#define OPENSL_IO + +#include +#include +#include +#include + +typedef struct threadLock_{ + pthread_mutex_t m; + pthread_cond_t c; + unsigned char s; +} threadLock; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct opensl_stream { + + // engine interfaces + SLObjectItf engineObject; + SLEngineItf engineEngine; + + // output mix interfaces + SLObjectItf outputMixObject; + + // buffer queue player interfaces + SLObjectItf bqPlayerObject; + SLPlayItf bqPlayerPlay; + SLVolumeItf bqPlayerVolume; + SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; + SLEffectSendItf bqPlayerEffectSend; + + // recorder interfaces + SLObjectItf recorderObject; + SLRecordItf recorderRecord; + SLVolumeItf recorderVolume; + SLAndroidSimpleBufferQueueItf recorderBufferQueue; + + // buffer indexes + int currentInputIndex; + int currentOutputIndex; + + // current buffer half (0, 1) + int currentOutputBuffer; + int currentInputBuffer; + + // buffers + short *outputBuffer[2]; + short *inputBuffer[2]; + + // size of buffers + int outBufSamples; + int inBufSamples; + + // locks + void* inlock; + void* outlock; + + double time; + int inchannels; + int outchannels; + int sr; + +} OPENSL_STREAM; + + /* + Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size + in frames. Returns a handle to the OpenSL stream + */ + OPENSL_STREAM* android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes); + /* + Close the audio device + */ + void android_CloseAudioDevice(OPENSL_STREAM *p); + /* + Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read. + */ + int android_AudioIn(OPENSL_STREAM *p, float *buffer,int size); + /* + Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written. + */ + int android_AudioOut(OPENSL_STREAM *p, short *buffer,int size); + /* + Get the current IO block time in seconds + */ + double android_GetTimestamp(OPENSL_STREAM *p); + /* + * Get the current input volume level. + */ + int android_GetInputVolume(OPENSL_STREAM *p); + /* + * Set the volume input level. + */ + void android_SetInputVolume(OPENSL_STREAM *p, int level); + /* + * Get the current output volume level. + */ + int android_GetOutputVolume(OPENSL_STREAM *p); + /* + * Set the volume output level. + */ + void android_SetOutputVolume(OPENSL_STREAM *p, int level); +#ifdef __cplusplus +}; +#endif + +#endif // #ifndef OPENSL_IO diff --git a/channels/rdpsnd/client/opensles/rdpsnd_opensl_es.c b/channels/rdpsnd/client/opensles/rdpsnd_opensl_es.c new file mode 100644 index 000000000..5d1789d34 --- /dev/null +++ b/channels/rdpsnd/client/opensles/rdpsnd_opensl_es.c @@ -0,0 +1,382 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Audio Output Virtual Channel + * + * Copyright 2013 Armin Novak + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "opensl_io.h" +#include "rdpsnd_main.h" + +typedef struct rdpsnd_opensles_plugin rdpsndopenslesPlugin; + +struct rdpsnd_opensles_plugin +{ + rdpsndDevicePlugin device; + + int latency; + int wformat; + int block_size; + char* device_name; + + OPENSL_STREAM *stream; + + UINT32 volume; + + UINT32 rate; + UINT32 channels; + int format; + FREERDP_DSP_CONTEXT* dsp_context; +}; + +static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, + UINT32 volume); + +static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles) +{ + DEBUG_SND("opensles=%p", opensles); + + return 0; +} + +static void rdpsnd_opensles_set_format(rdpsndDevicePlugin* device, + AUDIO_FORMAT* format, int latency) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency); + + if (format) + { + opensles->rate = format->nSamplesPerSec; + opensles->channels = format->nChannels; + + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + switch (format->wBitsPerSample) + { + case 4: + opensles->format = WAVE_FORMAT_ADPCM; + break; + + case 8: + opensles->format = WAVE_FORMAT_PCM; + break; + + case 16: + opensles->format = WAVE_FORMAT_ADPCM; + break; + } + break; + + case WAVE_FORMAT_ADPCM: + case WAVE_FORMAT_DVI_ADPCM: + opensles->format = WAVE_FORMAT_ADPCM; + break; + } + + opensles->wformat = format->wFormatTag; + opensles->block_size = format->nBlockAlign; + } + + opensles->latency = latency; + + rdpsnd_opensles_set_params(opensles); +} + +static void rdpsnd_opensles_open(rdpsndDevicePlugin* device, + AUDIO_FORMAT* format, int latency) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency); + if (opensles->stream) + return; + + opensles->stream = android_OpenAudioDevice( + opensles->rate, 0, opensles->channels, opensles->rate * 100); + if (!opensles->stream) + DEBUG_WARN("android_OpenAudioDevice failed"); + else + rdpsnd_opensles_set_volume(device, opensles->volume); +} + +static void rdpsnd_opensles_close(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + if (!opensles->stream) + return; + + android_CloseAudioDevice(opensles->stream); +} + +static void rdpsnd_opensles_free(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + + free(opensles->device_name); + + freerdp_dsp_context_free(opensles->dsp_context); + + free(opensles); +} + +static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device, + AUDIO_FORMAT* format) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p, format=%p", opensles, format); + + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + if (format->cbSize == 0 && + format->nSamplesPerSec <= 48000 && + (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && + (format->nChannels == 1 || format->nChannels == 2)) + { + return TRUE; + } + break; + + case WAVE_FORMAT_ADPCM: + case WAVE_FORMAT_DVI_ADPCM: + /* + if (format->nSamplesPerSec <= 48000 && + format->wBitsPerSample == 4 && + (format->nChannels == 1 || format->nChannels == 2)) + { + return TRUE; + } + */ + break; + + case WAVE_FORMAT_ALAW: + break; + + case WAVE_FORMAT_MULAW: + break; + + case WAVE_FORMAT_GSM610: + break; + } + + return FALSE; +} + +static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + + if (opensles->stream) + return android_GetOutputVolume(opensles->stream); + else + return opensles->volume; +} + +static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, + UINT32 value) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p, value=%d", opensles, value); + + opensles->volume = value; + if (opensles->stream) + android_SetOutputVolume(opensles->stream, value); +} + +static void rdpsnd_opensles_play(rdpsndDevicePlugin* device, + BYTE *data, int size) +{ + BYTE* src; + int len; + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p, data=%p, size=%d", opensles, data, size); + assert(opensles); + if (!opensles->stream) + return; + + if (opensles->format == WAVE_FORMAT_ADPCM) + { + opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context, + data, size, opensles->channels, opensles->block_size); + + size = opensles->dsp_context->adpcm_size; + src = opensles->dsp_context->adpcm_buffer; + } + else if (opensles->format == WAVE_FORMAT_DVI_ADPCM) + { + opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context, + data, size, opensles->channels, opensles->block_size); + + size = opensles->dsp_context->adpcm_size; + src = opensles->dsp_context->adpcm_buffer; + } + else + { + src = data; + } + + len = size; + while (size > 0) + { + int ret; + + if (len < 0) + break; + + if (len > size) + len = size; + + DEBUG_SND("len=%d, src=%p", len, src); + ret = android_AudioOut(opensles->stream, (short*)src, len / 2); + if (ret < 0) + { + DEBUG_WARN("android_AudioOut failed (%d)", ret); + break; + } + + DEBUG_SND("foobar XXXXXXXXXXXX opensles=%p, data=%p, size=%d", opensles, data, size); + + src += len; + size -= len; + } +} + +static void rdpsnd_opensles_start(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); +} + +static COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] = +{ + { "dev", COMMAND_LINE_VALUE_REQUIRED, "", + NULL, NULL, -1, NULL, "device" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } +}; + +static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device, + ADDIN_ARGV* args) +{ + int status; + DWORD flags; + COMMAND_LINE_ARGUMENT_A* arg; + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p, args=%p", opensles, args); + + flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON; + + status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, + rdpsnd_opensles_args, flags, opensles, NULL, NULL); + if (status < 0) + return status; + + arg = rdpsnd_opensles_args; + + do + { + if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT)) + continue; + + CommandLineSwitchStart(arg) + + CommandLineSwitchCase(arg, "dev") + { + opensles->device_name = _strdup(arg->Value); + } + + CommandLineSwitchEnd(arg) + } + while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); + + return status; +} + +#ifdef STATIC_CHANNELS +#define freerdp_rdpsnd_client_subsystem_entry \ + opensles_freerdp_rdpsnd_client_subsystem_entry +#endif + +int freerdp_rdpsnd_client_subsystem_entry( + PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) +{ + ADDIN_ARGV* args; + rdpsndopenslesPlugin* opensles; + + DEBUG_SND("pEntryPoints=%p", pEntryPoints); + + opensles = (rdpsndopenslesPlugin*) malloc(sizeof(rdpsndopenslesPlugin)); + ZeroMemory(opensles, sizeof(rdpsndopenslesPlugin)); + + opensles->device.Open = rdpsnd_opensles_open; + opensles->device.FormatSupported = rdpsnd_opensles_format_supported; + opensles->device.SetFormat = rdpsnd_opensles_set_format; + opensles->device.GetVolume = rdpsnd_opensles_get_volume; + opensles->device.SetVolume = rdpsnd_opensles_set_volume; + opensles->device.Start = rdpsnd_opensles_start; + opensles->device.Play = rdpsnd_opensles_play; + opensles->device.Close = rdpsnd_opensles_close; + opensles->device.Free = rdpsnd_opensles_free; + + args = pEntryPoints->args; + rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*) opensles, args); + + if (!opensles->device_name) + opensles->device_name = _strdup("default"); + + opensles->rate = 22050; + opensles->channels = 2; + opensles->format = WAVE_FORMAT_ADPCM; + + opensles->dsp_context = freerdp_dsp_context_new(); + + pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, + (rdpsndDevicePlugin*) opensles); + + DEBUG_SND("success"); + return 0; +} diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index b7099726a..844521317 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -647,7 +647,9 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin) rdpsnd->latency = -1; rdpsnd->queue = MessageQueue_New(); - rdpsnd->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, (void*) plugin, 0, NULL); + rdpsnd->thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, + (void*) plugin, 0, NULL); args = (ADDIN_ARGV*) plugin->channel_entry_points.pExtendedData; @@ -662,40 +664,59 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin) rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } +#if defined(WITH_IOSAUDIO) + if (!rdpsnd->device) + { + rdpsnd_set_subsystem(rdpsnd, "ios"); + rdpsnd_set_device_name(rdpsnd, ""); + rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); + } +#endif + +#if defined(WITH_OPENSLES) + if (!rdpsnd->device) + { + rdpsnd_set_subsystem(rdpsnd, "opensles"); + rdpsnd_set_device_name(rdpsnd, ""); + rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); + } +#endif + +#if defined(WITH_PULSE) if (!rdpsnd->device) { rdpsnd_set_subsystem(rdpsnd, "pulse"); rdpsnd_set_device_name(rdpsnd, ""); rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } +#endif +#if defined(WITH_ALSA) if (!rdpsnd->device) { rdpsnd_set_subsystem(rdpsnd, "alsa"); rdpsnd_set_device_name(rdpsnd, "default"); rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } +#endif +#if defined(WITH_MACAUDIO) if (!rdpsnd->device) { rdpsnd_set_subsystem(rdpsnd, "macaudio"); rdpsnd_set_device_name(rdpsnd, "default"); rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } +#endif +#if defined(WITH_WINMM) if (!rdpsnd->device) { rdpsnd_set_subsystem(rdpsnd, "winmm"); rdpsnd_set_device_name(rdpsnd, ""); rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); } - - if (!rdpsnd->device) - { - rdpsnd_set_subsystem(rdpsnd, "audiotrack"); - rdpsnd_set_device_name(rdpsnd, ""); - rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args); - } +#endif if (!rdpsnd->device) { diff --git a/channels/rdpsnd/client/rdpsnd_main.h b/channels/rdpsnd/client/rdpsnd_main.h index 19352d0f3..2c59cd569 100644 --- a/channels/rdpsnd/client/rdpsnd_main.h +++ b/channels/rdpsnd/client/rdpsnd_main.h @@ -22,6 +22,12 @@ #include +#if defined(WITH_DEBUG_SND) +#define DEBUG_SND(fmt, ...) DEBUG_CLASS("rdpsnd", fmt, ## __VA_ARGS__) +#else +#define DEBUG_SND(fmt, ...) do { } while (0) +#endif + struct _RDPSND_WAVE { BYTE* data;