diff --git a/CMakeLists.txt b/CMakeLists.txt index 0473b719e..96425d577 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,7 +213,6 @@ endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWINPR_EXPORTS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFREERDP_EXPORTS") - # Include files if(NOT IOS) check_include_files(fcntl.h HAVE_FCNTL_H) @@ -271,16 +270,32 @@ endif(APPLE) # Android if(ANDROID) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + add_definitions(-DNDK_DEBUG=1) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG}") + endif() + set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -llog") if (NOT FREERDP_ANDROID_EXTERNAL_SSL_PATH) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl") set(FREERDP_ANDROID_EXTERNAL_SSL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl") else() message(STATUS "FREERDP_ANDROID_EXTERNAL_SSL_PATH not set! - Needs to be set if openssl is not found in the android NDK (which usually isn't)") endif() + if(WITH_GPROF) + if (NOT FREERDP_ANDROID_EXTERNAL_PROFILER_PATH) + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler") + set(FREERDP_ANDROID_EXTERNAL_PROFILER_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler") + else() + message(STATUS "FREERDP_ANDROID_EXTERNAL_PROFILER_PATH not set!") + endif() + endif() endif() +endif() set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_ANDROID_EXTERNAL_SSL_PATH}) set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/libs/${ANDROID_ABI}) - CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake ${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh @ONLY) + CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/regenerate_jni_headers.sh @ONLY) endif() set(CMAKE_THREAD_PREFER_PTHREAD TRUE) @@ -313,6 +328,10 @@ set(OPENSSL_FEATURE_TYPE "REQUIRED") set(OPENSSL_FEATURE_PURPOSE "cryptography") set(OPENSSL_FEATURE_DESCRIPTION "encryption, certificate validation, hashing functions") +set(OPENSLES_FEATURE_TYPE "OPTIONAL") +set(OPENSLES_FEATURE_PURPOSE "multimedia") +set(OPENSLES_FEATURE_DESCRIPTION "OpenSLES audio / video") + set(ALSA_FEATURE_TYPE "RECOMMENDED") set(ALSA_FEATURE_PURPOSE "sound") set(ALSA_FEATURE_DESCRIPTION "audio input, audio output and multimedia redirection") @@ -359,6 +378,7 @@ if(WIN32) set(PCSC_FEATURE_TYPE "DISABLED") set(FFMPEG_FEATURE_TYPE "DISABLED") set(GSTREAMER_FEATURE_TYPE "DISABLED") + set(OPENSLES_FEATURE_TYPE "DISABLED") endif() if(APPLE) @@ -374,6 +394,7 @@ if(APPLE) set(PCSC_FEATURE_TYPE "DISABLED") set(GSTREAMER_FEATURE_TYPE "DISABLED") endif() + set(OPENSLES_FEATURE_TYPE "DISABLED") endif() if(ANDROID) @@ -385,6 +406,7 @@ if(ANDROID) set(PCSC_FEATURE_TYPE "DISABLED") set(FFMPEG_FEATURE_TYPE "DISABLED") set(GSTREAMER_FEATURE_TYPE "DISABLED") + set(OPENSLES_FEATURE_TYPE "REQUIRED") endif() @@ -398,6 +420,7 @@ endif() find_feature(ZLIB ${ZLIB_FEATURE_TYPE} ${ZLIB_FEATURE_PURPOSE} ${ZLIB_FEATURE_DESCRIPTION}) find_feature(OpenSSL ${OPENSSL_FEATURE_TYPE} ${OPENSSL_FEATURE_PURPOSE} ${OPENSSL_FEATURE_DESCRIPTION}) +find_feature(OpenSLES ${OPENSLES_FEATURE_TYPE} ${OPENSLES_FEATURE_PURPOSE} ${OPENSLES_FEATURE_DESCRIPTION}) find_feature(ALSA ${ALSA_FEATURE_TYPE} ${ALSA_FEATURE_PURPOSE} ${ALSA_FEATURE_DESCRIPTION}) find_feature(Pulse ${PULSE_FEATURE_TYPE} ${PULSE_FEATURE_PURPOSE} ${PULSE_FEATURE_DESCRIPTION}) @@ -451,6 +474,16 @@ set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..") +# Android profiling +if(ANDROID) + if(WITH_GPROF) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") + set(PROFILER_LIBRARIES + "${FREERDP_ANDROID_EXTERNAL_PROFILER_PATH}/obj/local/${ANDROID_ABI}/libandroid-ndk-profiler.a") + include_directories("${FREERDP_ANDROID_EXTERNAL_PROFILER_PATH}") + endif() +endif() + # Unit Tests include(CTest) diff --git a/channels/audin/client/CMakeLists.txt b/channels/audin/client/CMakeLists.txt index 303640640..0391ed5b6 100644 --- a/channels/audin/client/CMakeLists.txt +++ b/channels/audin/client/CMakeLists.txt @@ -47,3 +47,7 @@ endif() if(WITH_PULSE) add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "") endif() + +if(WITH_OPENSLES) + add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "") +endif() diff --git a/channels/audin/client/alsa/audin_alsa.c b/channels/audin/client/alsa/audin_alsa.c index 31dea232d..e8b7259a7 100644 --- a/channels/audin/client/alsa/audin_alsa.c +++ b/channels/audin/client/alsa/audin_alsa.c @@ -165,7 +165,12 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size if (WaitForSingleObject(alsa->stopEvent, 0) == WAIT_OBJECT_0) break; else + { + DEBUG_DVC("encoded %d [%d] to %d [%X]", alsa->buffer_frames, + tbytes_per_frame, encoded_size, + alsa->wformat); ret = alsa->receive(encoded_data, encoded_size, alsa->user_data); + } alsa->buffer_frames = 0; diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c index 1b1604490..b620126a7 100644 --- a/channels/audin/client/audin_main.c +++ b/channels/audin/client/audin_main.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include #include #include #include @@ -554,8 +555,10 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) ADDIN_ARGV* args; AUDIN_PLUGIN* audin; - audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin"); + assert(pEntryPoints); + assert(pEntryPoints->GetPlugin); + audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin"); if (audin == NULL) { audin = (AUDIN_PLUGIN*) malloc(sizeof(AUDIN_PLUGIN)); @@ -577,19 +580,32 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) if (audin->subsystem) audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args); +#if defined(WITH_PULSE) if (!audin->device) { audin_set_subsystem(audin, "pulse"); audin_set_device_name(audin, ""); audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args); } +#endif +#if defined(WITH_ALSA) if (!audin->device) { audin_set_subsystem(audin, "alsa"); audin_set_device_name(audin, "default"); audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args); } +#endif + +#if defined(WITH_OPENSLES) + if (!audin->device) + { + audin_set_subsystem(audin, "opensles"); + audin_set_device_name(audin, "default"); + audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args); + } +#endif if (audin->device == NULL) { diff --git a/channels/audin/client/opensles/CMakeLists.txt b/channels/audin/client/opensles/CMakeLists.txt new file mode 100644 index 000000000..01e527ebb --- /dev/null +++ b/channels/audin/client/opensles/CMakeLists.txt @@ -0,0 +1,44 @@ +# 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("audin" "opensles" "") + +set(${MODULE_PREFIX}_SRCS + opensl_io.c + audin_opensl_es.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/audin/client/opensles/audin_opensl_es.c b/channels/audin/client/opensles/audin_opensl_es.c new file mode 100644 index 000000000..50dbc19d8 --- /dev/null +++ b/channels/audin/client/opensles/audin_opensl_es.c @@ -0,0 +1,411 @@ +/** + * 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 + +#include "audin_main.h" +#include "opensl_io.h" + +typedef struct _AudinOpenSLESDevice +{ + IAudinDevice iface; + + char* device_name; + OPENSL_STREAM *stream; + + UINT32 frames_per_packet; + UINT32 rate; + UINT32 channels; + + UINT32 bytes_per_channel; + + UINT32 format; + UINT32 block_size; + + FREERDP_DSP_CONTEXT* dsp_context; + AudinReceive receive; + + HANDLE thread; + HANDLE stopEvent; + + void* user_data; +} AudinOpenSLESDevice; + +static void* audin_opensles_thread_func(void* arg) +{ + union + { + void *v; + short* s; + BYTE *b; + } buffer; + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg; + const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel; + + DEBUG_DVC("opensles=%p", opensles); + + assert(opensles); + assert(opensles->frames_per_packet > 0); + assert(opensles->dsp_context); + assert(opensles->stopEvent); + assert(opensles->stream); + + buffer.v = malloc(raw_size); + ZeroMemory(buffer.v, raw_size); + freerdp_dsp_context_reset_adpcm(opensles->dsp_context); + + while (!(WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0)) + { + size_t encoded_size; + void *encoded_data; + + int rc = android_RecIn(opensles->stream, buffer.s, raw_size); + if (rc < 0) + { + DEBUG_WARN("android_RecIn %d", rc); + continue; + } + + assert(rc == raw_size); + if (opensles->format == WAVE_FORMAT_ADPCM) + { + opensles->dsp_context->encode_ms_adpcm(opensles->dsp_context, + buffer.b, rc, opensles->channels, opensles->block_size); + + encoded_data = opensles->dsp_context->adpcm_buffer; + encoded_size = opensles->dsp_context->adpcm_size; + } + else if (opensles->format == WAVE_FORMAT_DVI_ADPCM) + { + opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context, + buffer.b, rc, + opensles->channels, opensles->block_size); + + encoded_data = opensles->dsp_context->adpcm_buffer; + encoded_size = opensles->dsp_context->adpcm_size; + } + else + { + encoded_data = buffer.v; + encoded_size = rc; + } + + rc = opensles->receive(encoded_data, encoded_size, opensles->user_data); + if (!rc) + break; + } + + free(buffer.v); + + DEBUG_DVC("thread shutdown."); + + ExitThread(0); + return NULL; +} + +static void audin_opensles_free(IAudinDevice* device) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p", device); + + /* The function may have been called out of order, + * ignore duplicate requests. */ + if (!opensles) + return; + + assert(opensles); + assert(opensles->dsp_context); + assert(!opensles->stream); + + freerdp_dsp_context_free(opensles->dsp_context); + + if (opensles->device_name) + free(opensles->device_name); + + free(opensles); +} + +static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p, format=%p", opensles, format); + + assert(format); + + switch (format->wFormatTag) + { + /* TODO: Deactivated, untested */ +#if 0 + case WAVE_FORMAT_PCM: /* PCM */ + if (format->cbSize == 0 && + (format->nSamplesPerSec <= 48000) && + (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && + (format->nChannels >= 1 && format->nChannels <= 2)) + { + return TRUE; + } + break; +#endif + /* TODO: Deactivated format, does not work, find out why */ +// case WAVE_FORMAT_ADPCM: /* IMA ADPCM */ + case WAVE_FORMAT_DVI_ADPCM: + if ((format->nSamplesPerSec <= 48000) && + (format->wBitsPerSample == 4) && + (format->nChannels == 1 || format->nChannels == 2)) + { + return TRUE; + } + break; + default: + DEBUG_DVC("Encoding '%s' [%08X] not supported", + rdpsnd_get_audio_tag_string(format->wFormatTag), + format->wFormatTag); + 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); + + assert(format); + + /* The function may have been called out of order, ignore + * requests before the device is available. */ + if (!opensles) + return; + + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + opensles->frames_per_packet = FramesPerPacket; + switch (format->wBitsPerSample) + { + case 4: + opensles->bytes_per_channel = 1; + break; + case 8: + opensles->bytes_per_channel = 1; + break; + case 16: + opensles->bytes_per_channel = 2; + break; + } + break; + case WAVE_FORMAT_ADPCM: + case WAVE_FORMAT_DVI_ADPCM: + opensles->bytes_per_channel = 2; + bs = (format->nBlockAlign - 4 * format->nChannels) * 4; + + opensles->frames_per_packet = + (FramesPerPacket * format->nChannels * 2 / + bs + 1) * bs / (format->nChannels * 2); + break; + case WAVE_FORMAT_ALAW: + case WAVE_FORMAT_MULAW: + opensles->frames_per_packet = FramesPerPacket; + break; + + default: + DEBUG_WARN("Encoding '%d' [%08X] not supported", + (format->wFormatTag), + format->wFormatTag); + return; + } + + opensles->rate = format->nSamplesPerSec; + opensles->channels = format->nChannels; + + opensles->format = format->wFormatTag; + opensles->block_size = format->nBlockAlign; + + DEBUG_DVC("aligned frames_per_packet=%d, block_size=%d", + opensles->frames_per_packet, opensles->block_size); +} + +static void audin_opensles_open(IAudinDevice* device, AudinReceive receive, + void* user_data) +{ + AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device; + + DEBUG_DVC("device=%p, receive=%d, user_data=%p", device, receive, user_data); + + assert(opensles); + + /* The function may have been called out of order, + * ignore duplicate open requests. */ + if(opensles->stream) + return; + + opensles->stream = android_OpenRecDevice( + opensles->device_name, + opensles->rate, + opensles->channels, + opensles->frames_per_packet, + opensles->bytes_per_channel * 8); + assert(opensles->stream); + + opensles->receive = receive; + opensles->user_data = user_data; + + 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); + + assert(opensles); + + /* The function may have been called out of order, + * ignore duplicate requests. */ + if (!opensles->stopEvent) + { + DEBUG_WARN("[ERROR] function called without matching open."); + return; + } + + assert(opensles->stopEvent); + assert(opensles->thread); + assert(opensles->stream); + + SetEvent(opensles->stopEvent); + WaitForSingleObject(opensles->thread, INFINITE); + CloseHandle(opensles->stopEvent); + CloseHandle(opensles->thread); + + android_CloseRecDevice(opensles->stream); + + opensles->stopEvent = NULL; + opensles->thread = NULL; + opensles->receive = NULL; + opensles->user_data = NULL; + opensles->stream = NULL; +} + +static 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); + + opensles->dsp_context = freerdp_dsp_context_new(); + + pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, + (IAudinDevice*) opensles); + + return 0; +} diff --git a/channels/audin/client/opensles/opensl_io.c b/channels/audin/client/opensles/opensl_io.c new file mode 100644 index 000000000..dda834b69 --- /dev/null +++ b/channels/audin/client/opensles/opensl_io.c @@ -0,0 +1,379 @@ +/* +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 "audin_main.h" +#include "opensl_io.h" +#define CONV16BIT 32768 +#define CONVMYFLT (1./32768.) + +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_DVC("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_DVC("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_DVC("engineEngine=%p", p->engineEngine); + if(result != SL_RESULT_SUCCESS) goto engine_end; + + // get the volume interface - important, this is optional! + result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_DEVICEVOLUME, &(p->deviceVolume)); + DEBUG_DVC("deviceVolume=%p", p->deviceVolume); + if(result != SL_RESULT_SUCCESS) + { + p->deviceVolume = NULL; + result = SL_RESULT_SUCCESS; + } + + engine_end: + assert(SL_RESULT_SUCCESS == result); + return result; +} + +// Open the OpenSL ES device for input +static SLresult openSLRecOpen(OPENSL_STREAM *p){ + + SLresult result; + SLuint32 sr = p->sr; + SLuint32 channels = p->inchannels; + + assert(!p->recorderObject); + + 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; + + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = channels; + format_pcm.samplesPerSec = sr; + format_pcm.channelMask = speakers; + format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; + + if (16 == p->bits_per_sample) + { + format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format_pcm.containerSize = 16; + } + else if (8 == p->bits_per_sample) + { + format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8; + format_pcm.containerSize = 8; + } + else + assert(0); + + SLDataSink audioSnk = {&loc_bq, &format_pcm}; + + // create audio recorder + // (requires the RECORD_AUDIO permission) + const SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + const SLboolean req[] = {SL_BOOLEAN_TRUE}; + result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine, + &(p->recorderObject), &audioSrc, &audioSnk, 1, id, req); + DEBUG_DVC("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_DVC("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_DVC("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_DVC("p->recorderBufferQueue=%p", p->recorderBufferQueue); + 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_DVC("p->recorderBufferQueue=%p", p->recorderBufferQueue); + assert(!result); + if (SL_RESULT_SUCCESS != result) + goto end_recopen; + + end_recopen: + return result; + } + else return SL_RESULT_SUCCESS; + + +} + +// close the OpenSL IO and destroy the audio engine +static void openSLDestroyEngine(OPENSL_STREAM *p) +{ + DEBUG_DVC("p=%p", p); + + // 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->recorderBufferQueue = 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 +OPENSL_STREAM *android_OpenRecDevice(char *name, int sr, int inchannels, + int bufferframes, int bits_per_sample) +{ + + OPENSL_STREAM *p; + p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1); + memset(p, 0, sizeof(OPENSL_STREAM)); + + p->inchannels = inchannels; + p->sr = sr; + p->queue = Queue_New(TRUE, -1, -1); + p->buffersize = bufferframes; + p->bits_per_sample = bits_per_sample; + + if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16)) + { + android_CloseRecDevice(p); + return NULL; + } + + if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) + { + android_CloseRecDevice(p); + return NULL; + } + + if(openSLRecOpen(p) != SL_RESULT_SUCCESS) + { + android_CloseRecDevice(p); + return NULL; + } + + return p; +} + +// close the android audio device +void android_CloseRecDevice(OPENSL_STREAM *p) +{ + DEBUG_DVC("p=%p", p); + + if (p == NULL) + return; + + if (p->queue) + { + while (Queue_Count(p->queue) > 0) + { + queue_element *e = Queue_Dequeue(p->queue); + free(e->data); + free(e); + } + Queue_Free(p->queue); + } + + if (p->next) + { + free(p->next->data); + free(p->next); + } + + if (p->prep) + { + free(p->prep->data); + free(p->prep); + } + + openSLDestroyEngine(p); + + free(p); +} + +// this callback handler is called every time a buffer finishes recording +void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + queue_element *e; + + OPENSL_STREAM *p = (OPENSL_STREAM *) context; + + DEBUG_DVC("p=%p", p); + + assert(p); + assert(p->next); + assert(p->prep); + assert(p->queue); + + e = calloc(1, sizeof(queue_element)); + e->data = calloc(p->buffersize, p->bits_per_sample / 8); + e->size = p->buffersize * p->bits_per_sample / 8; + + Queue_Enqueue(p->queue, p->next); + p->next = p->prep; + p->prep = e; + + (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, + e->data, e->size); + +} + +// gets a buffer of size samples from the device +int android_RecIn(OPENSL_STREAM *p,short *buffer,int size) +{ + queue_element *e; + int rc; + + assert(p); + assert(buffer); + assert(size > 0); + + /* Initial trigger for the queue. */ + if (!p->prep) + { + p->prep = calloc(1, sizeof(queue_element)); + p->prep->data = calloc(p->buffersize, p->bits_per_sample / 8); + p->prep->size = p->buffersize * p->bits_per_sample / 8; + + p->next = calloc(1, sizeof(queue_element)); + p->next->data = calloc(p->buffersize, p->bits_per_sample / 8); + p->next->size = p->buffersize * p->bits_per_sample / 8; + + (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, + p->next->data, p->next->size); + (*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue, + p->prep->data, p->prep->size); + + (*p->recorderRecord)->SetRecordState(p->recorderRecord, SL_RECORDSTATE_RECORDING); + } + + /* Wait for queue to be filled... */ + if (!Queue_Count(p->queue)) + WaitForSingleObject(p->queue->event, INFINITE); + + e = Queue_Dequeue(p->queue); + if (!e) + { + DEBUG_WARN("[ERROR] got e=%p from queue", e); + return -1; + } + + rc = (e->size < size) ? e->size : size; + assert(size == e->size); + assert(p->buffersize * p->bits_per_sample / 8 == size); + + memcpy(buffer, e->data, rc); + free(e->data); + free(e); + + return rc; +} + diff --git a/channels/audin/client/opensles/opensl_io.h b/channels/audin/client/opensles/opensl_io.h new file mode 100644 index 000000000..e76cd0664 --- /dev/null +++ b/channels/audin/client/opensles/opensl_io.h @@ -0,0 +1,92 @@ +/* +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 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + size_t size; + void *data; +} queue_element; + +typedef struct opensl_stream { + // engine interfaces + SLObjectItf engineObject; + SLEngineItf engineEngine; + + // device interfaces + SLDeviceVolumeItf deviceVolume; + + // recorder interfaces + SLObjectItf recorderObject; + SLRecordItf recorderRecord; + SLAndroidSimpleBufferQueueItf recorderBufferQueue; + + unsigned int inchannels; + unsigned int sr; + unsigned int buffersize; + unsigned int bits_per_sample; + + wQueue *queue; + queue_element *prep; + queue_element *next; +} 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_OpenRecDevice(char *name, int sr, int inchannels, + int bufferframes, int bits_per_sample); + /* + Close the audio device + */ + void android_CloseRecDevice(OPENSL_STREAM *p); + /* + Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read. + */ + int android_RecIn(OPENSL_STREAM *p, short *buffer,int size); +#ifdef __cplusplus +}; +#endif + +#endif // #ifndef OPENSL_IO diff --git a/channels/audin/client/pulse/audin_pulse.c b/channels/audin/client/pulse/audin_pulse.c index b6d2bbb42..b731c2246 100644 --- a/channels/audin/client/pulse/audin_pulse.c +++ b/channels/audin/client/pulse/audin_pulse.c @@ -324,8 +324,6 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length pulse->sample_spec.channels, pulse->block_size); encoded_data = pulse->dsp_context->adpcm_buffer; encoded_size = pulse->dsp_context->adpcm_size; - DEBUG_DVC("encoded %d to %d", - pulse->buffer_frames * pulse->bytes_per_frame, encoded_size); } else { @@ -333,6 +331,9 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length encoded_size = pulse->buffer_frames * pulse->bytes_per_frame; } + DEBUG_DVC("encoded %d [%d] to %d [%X]", + pulse->buffer_frames, pulse->bytes_per_frame, encoded_size, + pulse->format); ret = pulse->receive(encoded_data, encoded_size, pulse->user_data); pulse->buffer_frames = 0; if (!ret) @@ -410,7 +411,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u /* 500ms latency */ buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec); if (pa_stream_connect_record(pulse->stream, - pulse->device_name[0] ? pulse->device_name : NULL, + pulse->device_name, &buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0) { pa_threaded_mainloop_unlock(pulse->mainloop); @@ -447,7 +448,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u } } -COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = +static COMMAND_LINE_ARGUMENT_A audin_pulse_args[] = { { "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "audio device name" }, { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } @@ -505,9 +506,6 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt audin_pulse_parse_addin_args(pulse, args); - if (!pulse->device_name) - pulse->device_name = _strdup("default"); - pulse->dsp_context = freerdp_dsp_context_new(); pulse->mainloop = pa_threaded_mainloop_new(); diff --git a/channels/rdpsnd/client/CMakeLists.txt b/channels/rdpsnd/client/CMakeLists.txt index d741fa76d..8ec9bb336 100644 --- a/channels/rdpsnd/client/CMakeLists.txt +++ b/channels/rdpsnd/client/CMakeLists.txt @@ -58,3 +58,7 @@ endif() if(WITH_WINMM) add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "") endif() + +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..9cb29a99f --- /dev/null +++ b/channels/rdpsnd/client/opensles/opensl_io.c @@ -0,0 +1,351 @@ +/* +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 bqPlayerCallback(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; + + assert(p->engineObject); + assert(p->engineEngine); + + if(channels){ + // configure audio source + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = + { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, + p->queuesize + }; + + 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; +} + +// 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 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 and/or output +OPENSL_STREAM *android_OpenAudioDevice(int sr, int outchannels, int bufferframes){ + + OPENSL_STREAM *p; + p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1); + memset(p, 0, sizeof(OPENSL_STREAM)); + + p->queuesize = bufferframes; + p->outchannels = outchannels; + p->sr = sr; + + if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) { + android_CloseAudioDevice(p); + return NULL; + } + + if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) { + android_CloseAudioDevice(p); + return NULL; + } + + p->queue = Queue_New(TRUE, -1, -1); + + return p; +} + +// close the android audio device +void android_CloseAudioDevice(OPENSL_STREAM *p){ + + if (p == NULL) + return; + + openSLDestroyEngine(p); + if (p->queue) + Queue_Free(p->queue); + + free(p); +} + +// this callback handler is called every time a buffer finishes playing +void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + OPENSL_STREAM *p = (OPENSL_STREAM *) context; + + assert(p); + assert(p->queue); + + void *data = Queue_Dequeue(p->queue); + free(data); +} + +// puts a buffer of size samples to the device +int android_AudioOut(OPENSL_STREAM *p, const short *buffer,int size) +{ + assert(p); + assert(buffer); + assert(size > 0); + + /* Assure, that the queue is not full. */ + if (p->queuesize <= Queue_Count(p->queue)) + WaitForSingleObject(p->queue->event, INFINITE); + + void *data = calloc(size, sizeof(short)); + memcpy(data, buffer, size * sizeof(short)); + (*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue, + data, sizeof(short) * size); + + return size; +} + +int android_GetOutputMute(OPENSL_STREAM *p) { + SLboolean mute; + + assert(p); + assert(p->bqPlayerVolume); + + SLresult rc = (*p->bqPlayerVolume)->GetMute(p->bqPlayerVolume, &mute); + assert(SL_RESULT_SUCCESS == rc); + + return mute; +} + +void android_SetOutputMute(OPENSL_STREAM *p, BOOL _mute) { + SLboolean mute = _mute; + + assert(p); + assert(p->bqPlayerVolume); + + SLresult rc = (*p->bqPlayerVolume)->SetMute(p->bqPlayerVolume, mute); + 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; +} + +int android_GetOutputVolumeMax(OPENSL_STREAM *p){ + SLmillibel level; + + assert(p); + assert(p->bqPlayerVolume); + + SLresult rc = (*p->bqPlayerVolume)->GetMaxVolumeLevel(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); +} + diff --git a/channels/rdpsnd/client/opensles/opensl_io.h b/channels/rdpsnd/client/opensles/opensl_io.h new file mode 100644 index 000000000..c26aa0795 --- /dev/null +++ b/channels/rdpsnd/client/opensles/opensl_io.h @@ -0,0 +1,106 @@ +/* +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 + +#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; + + unsigned int outchannels; + unsigned int sr; + + unsigned int queuesize; + wQueue *queue; +} OPENSL_STREAM; + + /* + Open the audio device with a given sampling rate (sr), output channels and IO buffer size + in frames. Returns a handle to the OpenSL stream + */ + OPENSL_STREAM* android_OpenAudioDevice(int sr, int outchannels, int bufferframes); + /* + Close the audio device + */ + void android_CloseAudioDevice(OPENSL_STREAM *p); + /* + Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written. + */ + int android_AudioOut(OPENSL_STREAM *p, const short *buffer, int size); + /* + * Set the volume input level. + */ + void android_SetOutputVolume(OPENSL_STREAM *p, int level); + /* + * Get the current output mute setting. + */ + int android_GetOutputMute(OPENSL_STREAM *p); + /* + * Change the current output mute setting. + */ + void android_SetOutputMute(OPENSL_STREAM *p, BOOL mute); + /* + * Get the current output volume level. + */ + int android_GetOutputVolume(OPENSL_STREAM *p); + /* + * Get the maximum output volume level. + */ + int android_GetOutputVolumeMax(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_opensles.c b/channels/rdpsnd/client/opensles/rdpsnd_opensles.c new file mode 100644 index 000000000..d836afa33 --- /dev/null +++ b/channels/rdpsnd/client/opensles/rdpsnd_opensles.c @@ -0,0 +1,469 @@ +/** + * 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 + +#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 int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max) +{ + const int min = SL_MILLIBEL_MIN; + const int step = max - min; + const int rc = (level * step / 0xFFFF) + min; + + DEBUG_SND("level=%d, min=%d, max=%d, step=%d, result=%d", + level, min, max, step, rc); + + return rc; +} + +static unsigned short rdpsnd_opensles_millibel_to_volume(int millibel, int max) +{ + const int min = SL_MILLIBEL_MIN; + const int range = max - min; + const int rc = ((millibel - min) * 0xFFFF + range / 2 + 1) / range; + + DEBUG_SND("millibel=%d, min=%d, max=%d, range=%d, result=%d", + millibel, min, max, range, rc); + + return rc; +} + +static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin *hdl) +{ + bool rc = true; + + if (!hdl) + rc = false; + else + { + if (!hdl->dsp_context) + rc = false; + if (!hdl->stream) + rc = false; + } + + return rc; +} + +static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, + UINT32 volume); + +static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles) +{ + DEBUG_SND("opensles=%p", opensles); + if (!rdpsnd_opensles_check_handle(opensles)) + return 0; + + if (opensles->stream) + android_CloseAudioDevice(opensles->stream); + + opensles->stream = android_OpenAudioDevice( + opensles->rate, opensles->channels, 20); + + return 0; +} + +static void rdpsnd_opensles_set_format(rdpsndDevicePlugin* device, + AUDIO_FORMAT* format, int latency) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + rdpsnd_opensles_check_handle(opensles); + + DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency); + + if (format) + { + DEBUG_SND("format=%d, cbsize=%d, samples=%d, bits=%d, channels=%d, align=%d", + format->wFormatTag, format->cbSize, format->nSamplesPerSec, + format->wBitsPerSample, format->nChannels, format->nBlockAlign); + + 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 = format->wFormatTag; + 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, rate=%d", + opensles, format, latency, opensles->rate); + + if( rdpsnd_opensles_check_handle(opensles)) + return; + + opensles->stream = android_OpenAudioDevice( + opensles->rate, opensles->channels, 20); + assert(opensles->stream); + + if (!opensles->stream) + DEBUG_WARN("android_OpenAudioDevice failed"); + else + rdpsnd_opensles_set_volume(device, opensles->volume); + + rdpsnd_opensles_set_format(device, format, latency); +} + +static void rdpsnd_opensles_close(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + if( !rdpsnd_opensles_check_handle(opensles)) + return; + + android_CloseAudioDevice(opensles->stream); + opensles->stream = NULL; +} + +static void rdpsnd_opensles_free(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + assert(opensles); + + assert(opensles->device_name); + free(opensles->device_name); + + assert(opensles->dsp_context); + 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("format=%d, cbsize=%d, samples=%d, bits=%d, channels=%d, align=%d", + format->wFormatTag, format->cbSize, format->nSamplesPerSec, + format->wBitsPerSample, format->nChannels, format->nBlockAlign); + + assert(opensles); + assert(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: + case WAVE_FORMAT_MULAW: + case WAVE_FORMAT_GSM610: + default: + break; + } + + return FALSE; +} + +static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p", opensles); + assert(opensles); + + if (opensles->stream) + { + const int max = android_GetOutputVolumeMax(opensles->stream); + const int rc = android_GetOutputVolume(opensles->stream); + + if (android_GetOutputMute(opensles->stream)) + opensles->volume = 0; + else + { + const unsigned short vol = rdpsnd_opensles_millibel_to_volume(rc, max); + opensles->volume = (vol << 16) | (vol & 0xFFFF); + } + } + + 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); + assert(opensles); + + opensles->volume = value; + + if (opensles->stream) + { + if (0 == opensles->volume) + android_SetOutputMute(opensles->stream, true); + else + { + const int max = android_GetOutputVolumeMax(opensles->stream); + const int vol = rdpsnd_opensles_volume_to_millibel(value & 0xFFFF, max); + + android_SetOutputMute(opensles->stream, false); + android_SetOutputVolume(opensles->stream, vol); + } + } +} + +static void rdpsnd_opensles_play(rdpsndDevicePlugin* device, + BYTE *data, int size) +{ + union + { + BYTE *b; + short *s; + } src; + int ret; + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + + DEBUG_SND("opensles=%p, data=%p, size=%d", opensles, data, size); + if (!rdpsnd_opensles_check_handle(opensles)) + return; + + if (opensles->format == WAVE_FORMAT_ADPCM) + { + DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d", + opensles->dsp_context, opensles->channels, opensles->block_size); + + opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context, + data, size, opensles->channels, opensles->block_size); + + size = opensles->dsp_context->adpcm_size; + src.b = opensles->dsp_context->adpcm_buffer; + } + else if (opensles->format == WAVE_FORMAT_DVI_ADPCM) + { + DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d", + opensles->dsp_context, opensles->channels, opensles->block_size); + + opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context, + data, size, opensles->channels, opensles->block_size); + + size = opensles->dsp_context->adpcm_size; + src.b = opensles->dsp_context->adpcm_buffer; + } + else + { + src.b = data; + } + + DEBUG_SND("size=%d, src=%p", size, src.b); + assert(0 == size % 2); + assert(size > 0); + assert(src.b); + + ret = android_AudioOut(opensles->stream, src.s, size / 2); + if (ret < 0) + DEBUG_WARN("android_AudioOut failed (%d)", ret); +} + +static void rdpsnd_opensles_start(rdpsndDevicePlugin* device) +{ + rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device; + rdpsnd_opensles_check_handle(opensles); + + 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; + + assert(opensles); + assert(args); + + 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 = 44100; + 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 cda1cf2ce..e3707be74 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -83,7 +83,7 @@ struct rdpsnd_plugin rdpsndDevicePlugin* device; }; -void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo); +static void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo); static void* rdpsnd_schedule_thread(void* arg) { @@ -117,6 +117,7 @@ static void* rdpsnd_schedule_thread(void* arg) rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo); free(wave); + message.wParam = NULL; } return NULL; @@ -258,13 +259,14 @@ void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s) UINT16 wVersion; AUDIO_FORMAT* format; UINT16 wNumberOfFormats; + UINT32 dwVolume; rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); rdpsnd->NumberOfServerFormats = 0; rdpsnd->ServerFormats = NULL; Stream_Seek_UINT32(s); /* dwFlags */ - Stream_Seek_UINT32(s); /* dwVolume */ + Stream_Read_UINT32(s, dwVolume); /* dwVolume */ Stream_Seek_UINT32(s); /* dwPitch */ Stream_Seek_UINT16(s); /* wDGramPort */ Stream_Read_UINT16(s, wNumberOfFormats); @@ -297,6 +299,9 @@ void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s) if (wVersion >= 6) rdpsnd_send_quality_mode_pdu(rdpsnd); + + if (rdpsnd->device) + IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume); } void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, UINT16 wPackSize) @@ -379,7 +384,7 @@ void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu); } -void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) +static void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) { MessageQueue_Post(device->rdpsnd->queue, NULL, 0, (void*) wave, NULL); } @@ -649,7 +654,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; @@ -664,33 +671,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); } +#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; diff --git a/client/Android/CMakeLists.txt b/client/Android/CMakeLists.txt index 9557f7e63..0930be3f3 100644 --- a/client/Android/CMakeLists.txt +++ b/client/Android/CMakeLists.txt @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +set(ANDROID_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/aFreeRDP") + if(ANDROID_BUILD_JAVA) if (NOT ANDROID_SDK) message(FATAL_ERROR "ANDROID_SDK not set but required for building the java gui (ANDROID_BUILD_JAVA)") @@ -36,5 +38,11 @@ if(ANDROID_BUILD_JAVA) endif() endif(ANDROID_BUILD_JAVA) +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(ANDROID_DEBUG_ENABLE "true") +else() + set(ANDROID_DEBUG_ENABLE "false") +endif() + add_subdirectory(FreeRDPCore) add_subdirectory(aFreeRDP) diff --git a/client/Android/FreeRDPCore/.classpath b/client/Android/FreeRDPCore/.classpath index a4763d1ee..aacc761ec 100644 --- a/client/Android/FreeRDPCore/.classpath +++ b/client/Android/FreeRDPCore/.classpath @@ -1,8 +1,7 @@ - - - - + + + diff --git a/client/Android/FreeRDPCore/AndroidManifest.xml.cmake b/client/Android/FreeRDPCore/AndroidManifest.xml.cmake index f21e53c74..a9adc61e2 100644 --- a/client/Android/FreeRDPCore/AndroidManifest.xml.cmake +++ b/client/Android/FreeRDPCore/AndroidManifest.xml.cmake @@ -11,6 +11,8 @@ + + diff --git a/client/Android/FreeRDPCore/CMakeLists.txt b/client/Android/FreeRDPCore/CMakeLists.txt index 6f256af64..7c90dc965 100644 --- a/client/Android/FreeRDPCore/CMakeLists.txt +++ b/client/Android/FreeRDPCore/CMakeLists.txt @@ -16,8 +16,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(ANDROID_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(ANDROID_PACKAGE_NAME "aFreeRDPCore") +file(MAKE_DIRECTORY ${ANDROID_BINARY_DIR}) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake ${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml @ONLY) @@ -39,6 +39,7 @@ add_subdirectory(jni) if(ANDROID_BUILD_JAVA) + file(MAKE_DIRECTORY "${ANDROID_BINARY_DIR}/bin") set(ANDROIDLIB "${ANDROID_BINARY_DIR}/bin/classes.jar") # command to create the android package diff --git a/client/Android/FreeRDPCore/jni/CMakeLists.txt b/client/Android/FreeRDPCore/jni/CMakeLists.txt index 05e6e1ff5..5fa6ec505 100644 --- a/client/Android/FreeRDPCore/jni/CMakeLists.txt +++ b/client/Android/FreeRDPCore/jni/CMakeLists.txt @@ -72,3 +72,12 @@ set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${ANDROID_BINARY_DIR}/libs/${ANDROID_ABI}") set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Android") + +get_property(LIB_ABSNAME TARGET ${MODULE_NAME} PROPERTY LOCATION) +file(MAKE_DIRECTORY ${ANDROID_BINARY_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}) + +add_custom_command(TARGET ${MODULE_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${LIB_ABSNAME} + ${ANDROID_BINARY_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/) + + diff --git a/client/Android/FreeRDPCore/jni/android_freerdp.c b/client/Android/FreeRDPCore/jni/android_freerdp.c index f0046dcc5..bfdf6a409 100644 --- a/client/Android/FreeRDPCore/jni/android_freerdp.c +++ b/client/Android/FreeRDPCore/jni/android_freerdp.c @@ -12,6 +12,7 @@ #include "config.h" #endif +#include #include #include #include @@ -34,6 +35,10 @@ #include "android_debug.h" #include "android_cliprdr.h" +#if defined(WITH_GPROF) +#include "jni/prof.h" +#endif + struct thread_data { freerdp* instance; @@ -262,6 +267,8 @@ int android_freerdp_run(freerdp* instance) int select_status; struct timeval timeout; + assert(instance); + memset(rfds, 0, sizeof(rfds)); memset(wfds, 0, sizeof(wfds)); @@ -329,6 +336,9 @@ int android_freerdp_run(freerdp* instance) break; } } + + if (freerdp_shall_disconnect(instance)) + break; if (freerdp_check_fds(instance) != TRUE) { @@ -365,6 +375,9 @@ void* android_thread_func(void* param) struct thread_data* data; data = (struct thread_data*) param; + assert(data); + assert(data->instance); + freerdp* instance = data->instance; android_freerdp_run(instance); free(data); @@ -378,6 +391,10 @@ JNIEXPORT jint JNICALL jni_freerdp_new(JNIEnv *env, jclass cls) { freerdp* instance; +#if defined(WITH_GPROF) + monstartup("libfreerdp-android.so"); +#endif + // create instance instance = freerdp_new(); instance->PreConnect = android_pre_connect; @@ -401,6 +418,10 @@ JNIEXPORT void JNICALL jni_freerdp_free(JNIEnv *env, jclass cls, jint instance) { freerdp* inst = (freerdp*)instance; freerdp_free(inst); + +#if defined(WITH_GPROF) + moncleanup(); +#endif } JNIEXPORT jboolean JNICALL jni_freerdp_connect(JNIEnv *env, jclass cls, jint instance) @@ -409,6 +430,10 @@ JNIEXPORT jboolean JNICALL jni_freerdp_connect(JNIEnv *env, jclass cls, jint ins struct thread_data* data = (struct thread_data*) malloc(sizeof(struct thread_data)); data->instance = inst; + assert(inst); + assert(data); + assert(inst->context); + androidContext* ctx = (androidContext*)inst->context; pthread_create(&ctx->thread, 0, android_thread_func, data); @@ -654,6 +679,52 @@ JNIEXPORT void JNICALL jni_freerdp_set_drive_redirection(JNIEnv *env, jclass cls (*env)->ReleaseStringUTFChars(env, jpath, path); } +JNIEXPORT void JNICALL jni_freerdp_set_sound_redirection(JNIEnv *env, + jclass cls, jint instance, jint redirect) +{ + char** p; + int count = 1; + freerdp* inst = (freerdp*)instance; + rdpSettings * settings = inst->settings; + + DEBUG_ANDROID("sound: %s", + redirect ? ((redirect == 1) ? "Server" : "Redirect") : "None"); + + settings->AudioPlayback = (redirect == 2) ? TRUE : FALSE; + settings->RemoteConsoleAudio = (redirect == 1) ? TRUE : FALSE; + if (settings->AudioPlayback) + { + p = malloc(sizeof(char*)); + p[0] = "rdpsnd"; + + freerdp_client_add_static_channel(settings, count, p); + + free(p); + } +} + +JNIEXPORT void JNICALL jni_freerdp_set_microphone_redirection(JNIEnv *env, + jclass cls, jint instance, jboolean enable) +{ + char** p; + int count = 1; + freerdp* inst = (freerdp*)instance; + rdpSettings * settings = inst->settings; + + DEBUG_ANDROID("microphone redirect: %s", enable ? "TRUE" : "FALSE"); + + settings->AudioCapture = enable; + if (enable) + { + p = malloc(sizeof(char*)); + p[0] = "audin"; + + freerdp_client_add_dynamic_channel(settings, count, p); + + free(p); + } +} + JNIEXPORT void JNICALL jni_freerdp_set_clipboard_redirection(JNIEnv *env, jclass cls, jint instance, jboolean enable) { freerdp* inst = (freerdp*)instance; diff --git a/client/Android/FreeRDPCore/jni/android_freerdp.h b/client/Android/FreeRDPCore/jni/android_freerdp.h index ad96bc398..98c94478d 100644 --- a/client/Android/FreeRDPCore/jni/android_freerdp.h +++ b/client/Android/FreeRDPCore/jni/android_freerdp.h @@ -45,6 +45,8 @@ JNIEXPORT void JNICALL jni_freerdp_set_performance_flags(JNIEnv *env, jclass cls jboolean disableMenuAnimations, jboolean disableTheming, jboolean enableFontSmoothing, jboolean enableDesktopComposition); JNIEXPORT void JNICALL jni_freerdp_set_advanced_settings(JNIEnv *env, jclass cls, jint instance, jstring jRemoteProgram, jstring jWorkDir); JNIEXPORT void JNICALL jni_freerdp_set_drive_redirection(JNIEnv *env, jclass cls, jint instance, jstring jpath); +JNIEXPORT void JNICALL jni_freerdp_set_sound_redirection(JNIEnv *env, jclass cls, jint instance, jint redirect); +JNIEXPORT void JNICALL jni_freerdp_set_microphone_redirection(JNIEnv *env, jclass cls, jint instance, jboolean enable); JNIEXPORT void JNICALL jni_freerdp_set_clipboard_redirection(JNIEnv *env, jclass cls, jint instance, jboolean enable); JNIEXPORT void JNICALL jni_freerdp_set_gateway_info(JNIEnv *env, jclass cls, jint instance, jstring jgatewayhostname, jint port, jstring jgatewayusername, jstring jgatewaypassword, jstring jgatewaydomain); JNIEXPORT void JNICALL jni_freerdp_set_data_directory(JNIEnv *env, jclass cls, jint instance, jstring jdirectory); diff --git a/client/Android/FreeRDPCore/jni/generated/android_freerdp_jni.c b/client/Android/FreeRDPCore/jni/generated/android_freerdp_jni.c index 1aa889abe..597285aa8 100644 --- a/client/Android/FreeRDPCore/jni/generated/android_freerdp_jni.c +++ b/client/Android/FreeRDPCore/jni/generated/android_freerdp_jni.c @@ -76,6 +76,18 @@ JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_ jni_freerdp_set_clipboard_redirection(env, cls, inst, enable); } +JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1sound_1redirection + (JNIEnv *env, jclass cls, jint inst, jint redirect) +{ + jni_freerdp_set_sound_redirection(env, cls, inst, redirect); +} + +JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1microphone_1redirection + (JNIEnv *env, jclass cls, jint inst, jboolean redirect) +{ + jni_freerdp_set_microphone_redirection(env, cls, inst, redirect); +} + JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1drive_1redirection (JNIEnv *env, jclass cls, jint inst, jstring path) { diff --git a/client/Android/FreeRDPCore/jni/generated/com_freerdp_freerdpcore_services_LibFreeRDP.h b/client/Android/FreeRDPCore/jni/generated/com_freerdp_freerdpcore_services_LibFreeRDP.h index 1e6be8dff..868b49518 100644 --- a/client/Android/FreeRDPCore/jni/generated/com_freerdp_freerdpcore_services_LibFreeRDP.h +++ b/client/Android/FreeRDPCore/jni/generated/com_freerdp_freerdpcore_services_LibFreeRDP.h @@ -87,6 +87,22 @@ JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_ JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1clipboard_1redirection (JNIEnv *, jclass, jint, jboolean); +/* + * Class: com_freerdp_freerdpcore_services_LibFreeRDP + * Method: freerdp_set_sound_redirection + * Signature: (IZ)V + */ +JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1sound_1redirection + (JNIEnv *, jclass, jint, jint); + +/* + * Class: com_freerdp_freerdpcore_services_LibFreeRDP + * Method: freerdp_set_microphone_redirection + * Signature: (IZ)V + */ +JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1microphone_1redirection + (JNIEnv *, jclass, jint, jboolean); + /* * Class: com_freerdp_freerdpcore_services_LibFreeRDP * Method: freerdp_set_drive_redirection diff --git a/client/Android/FreeRDPCore/res/values-es/strings.xml b/client/Android/FreeRDPCore/res/values-es/strings.xml index e539660de..6e7f033b3 100644 --- a/client/Android/FreeRDPCore/res/values-es/strings.xml +++ b/client/Android/FreeRDPCore/res/values-es/strings.xml @@ -115,6 +115,18 @@ Enable Gateway Gateway Settings Redirect SDCard + Redirect Sound + + Do not play + Play on Server + Play on Device + + + 0 + 1 + 2 + + Redirect Microphone Seguridad Automatico diff --git a/client/Android/FreeRDPCore/res/values-fr/strings.xml b/client/Android/FreeRDPCore/res/values-fr/strings.xml index ffd74bf32..35691fdd8 100644 --- a/client/Android/FreeRDPCore/res/values-fr/strings.xml +++ b/client/Android/FreeRDPCore/res/values-fr/strings.xml @@ -114,6 +114,18 @@ Enable Gateway Gateway Settings "Redirect SDCard" + Redirect Sound + + Do not play + Play on Server + Play on Device + + + 0 + 1 + 2 + + Redirect Microphone "Securité" "Automatique" diff --git a/client/Android/FreeRDPCore/res/values-nl/strings.xml b/client/Android/FreeRDPCore/res/values-nl/strings.xml index 03c694aa5..4a58821ee 100644 --- a/client/Android/FreeRDPCore/res/values-nl/strings.xml +++ b/client/Android/FreeRDPCore/res/values-nl/strings.xml @@ -115,6 +115,18 @@ Gateway inschakelen Gateway instellingen SDCard aankoppelen + Redirect Sound + + Do not play + Play on Server + Play on Device + + + 0 + 1 + 2 + + Redirect Microphone Beveiliging Automatisch diff --git a/client/Android/FreeRDPCore/res/values/strings.xml b/client/Android/FreeRDPCore/res/values/strings.xml index 0f6aacded..a08cb94bc 100644 --- a/client/Android/FreeRDPCore/res/values/strings.xml +++ b/client/Android/FreeRDPCore/res/values/strings.xml @@ -112,6 +112,18 @@ Enable Gateway Gateway Settings Redirect SDCard + Redirect Sound + + Do not play + Play on Server + Play on Device + + + 0 + 1 + 2 + + Redirect Microphone Security Automatic diff --git a/client/Android/FreeRDPCore/res/xml/advanced_settings.xml b/client/Android/FreeRDPCore/res/xml/advanced_settings.xml index 61cd61d19..77e819732 100644 --- a/client/Android/FreeRDPCore/res/xml/advanced_settings.xml +++ b/client/Android/FreeRDPCore/res/xml/advanced_settings.xml @@ -36,6 +36,8 @@ + + diff --git a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/application/GlobalApp.java b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/application/GlobalApp.java index aca737a4b..e9c7f49a6 100644 --- a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/application/GlobalApp.java +++ b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/application/GlobalApp.java @@ -82,6 +82,7 @@ public class GlobalApp extends Application implements LibFreeRDP.EventListener super.onCreate(); bookmarkDB = new BookmarkDB(this); + manualBookmarkGateway = new ManualBookmarkGateway(bookmarkDB); historyDB = new HistoryDB(this); diff --git a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/domain/BookmarkBase.java b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/domain/BookmarkBase.java index 8db15b864..9a9f54a0d 100644 --- a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/domain/BookmarkBase.java +++ b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/domain/BookmarkBase.java @@ -295,6 +295,8 @@ public class BookmarkBase implements Parcelable, Cloneable private ScreenSettings screen3G; private PerformanceFlags performance3G; private boolean redirectSDCard; + private int redirectSound; + private boolean redirectMicrophone; private int security; private boolean consoleMode; private String remoteProgram; @@ -309,6 +311,8 @@ public class BookmarkBase implements Parcelable, Cloneable screen3G = parcel.readParcelable(ScreenSettings.class.getClassLoader()); performance3G = parcel.readParcelable(PerformanceFlags.class.getClassLoader()); redirectSDCard = (parcel.readInt() == 1) ? true : false; + redirectSound = parcel.readInt(); + redirectMicrophone = (parcel.readInt() == 1) ? true : false; security = parcel.readInt(); consoleMode = (parcel.readInt() == 1) ? true : false; remoteProgram = parcel.readString(); @@ -320,6 +324,8 @@ public class BookmarkBase implements Parcelable, Cloneable screen3G = new ScreenSettings(); performance3G = new PerformanceFlags(); redirectSDCard = false; + redirectSound = 0; + redirectMicrophone = false; security = 0; consoleMode = false; remoteProgram = ""; @@ -358,6 +364,22 @@ public class BookmarkBase implements Parcelable, Cloneable return redirectSDCard; } + public void setRedirectSound(int redirect) { + this.redirectSound = redirect; + } + + public int getRedirectSound() { + return redirectSound; + } + + public void setRedirectMicrophone(boolean redirect) { + this.redirectMicrophone = redirect; + } + + public boolean getRedirectMicrophone() { + return redirectMicrophone; + } + public void setSecurity(int security) { this.security = security; } @@ -418,6 +440,8 @@ public class BookmarkBase implements Parcelable, Cloneable out.writeParcelable(screen3G, flags); out.writeParcelable(performance3G, flags); out.writeInt(redirectSDCard ? 1 : 0); + out.writeInt(redirectSound); + out.writeInt(redirectMicrophone ? 1 : 0); out.writeInt(security); out.writeInt(consoleMode ? 1 : 0); out.writeString(remoteProgram); @@ -620,6 +644,8 @@ public class BookmarkBase implements Parcelable, Cloneable editor.putBoolean("bookmark.perf_themes_3g", advancedSettings.getPerformance3G().getTheming()); editor.putBoolean("bookmark.redirect_sdcard", advancedSettings.getRedirectSDCard()); + editor.putInt("bookmark.redirect_sound", advancedSettings.getRedirectSound()); + editor.putBoolean("bookmark.redirect_microphone", advancedSettings.getRedirectMicrophone()); editor.putInt("bookmark.security", advancedSettings.getSecurity()); editor.putString("bookmark.remote_program", advancedSettings.getRemoteProgram()); editor.putString("bookmark.work_dir", advancedSettings.getWorkDir()); @@ -662,6 +688,8 @@ public class BookmarkBase implements Parcelable, Cloneable advancedSettings.getPerformance3G().setTheming(sharedPrefs.getBoolean("bookmark.perf_themes_3g", false)); advancedSettings.setRedirectSDCard(sharedPrefs.getBoolean("bookmark.redirect_sdcard", false)); + advancedSettings.setRedirectSound(sharedPrefs.getInt("bookmark.redirect_sound", 0)); + advancedSettings.setRedirectMicrophone(sharedPrefs.getBoolean("bookmark.redirect_microphone", false)); advancedSettings.setSecurity(sharedPrefs.getInt("bookmark.security", 0)); advancedSettings.setRemoteProgram(sharedPrefs.getString("bookmark.remote_program", "")); advancedSettings.setWorkDir(sharedPrefs.getString("bookmark.work_dir", "")); diff --git a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkBaseGateway.java b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkBaseGateway.java index 7f6a939b1..593cecdf8 100644 --- a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkBaseGateway.java +++ b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkBaseGateway.java @@ -64,6 +64,8 @@ public abstract class BookmarkBaseGateway rowid = insertPerformanceFlags(db, bookmark.getAdvancedSettings().getPerformance3G()); values.put("performance_3g", rowid); values.put("redirect_sdcard", bookmark.getAdvancedSettings().getRedirectSDCard()); + values.put("redirect_sound", bookmark.getAdvancedSettings().getRedirectSound()); + values.put("redirect_microphone", bookmark.getAdvancedSettings().getRedirectMicrophone()); values.put("security", bookmark.getAdvancedSettings().getSecurity()); values.put("console_mode", bookmark.getAdvancedSettings().getConsoleMode()); values.put("remote_program", bookmark.getAdvancedSettings().getRemoteProgram()); @@ -100,6 +102,8 @@ public abstract class BookmarkBaseGateway updateScreenSettings3G(db, bookmark); updatePerformanceFlags3G(db, bookmark); values.put("redirect_sdcard", bookmark.getAdvancedSettings().getRedirectSDCard()); + values.put("redirect_sound", bookmark.getAdvancedSettings().getRedirectSound()); + values.put("redirect_microphone", bookmark.getAdvancedSettings().getRedirectMicrophone()); values.put("security", bookmark.getAdvancedSettings().getSecurity()); values.put("console_mode", bookmark.getAdvancedSettings().getConsoleMode()); values.put("remote_program", bookmark.getAdvancedSettings().getRemoteProgram()); @@ -219,6 +223,8 @@ public abstract class BookmarkBaseGateway // advanced settings columns.add("enable_3g_settings"); columns.add("redirect_sdcard"); + columns.add("redirect_sound"); + columns.add("redirect_microphone"); columns.add("security"); columns.add("console_mode"); columns.add("remote_program"); @@ -277,6 +283,8 @@ public abstract class BookmarkBaseGateway readScreenSettings3G(bookmark, cursor); readPerformanceFlags3G(bookmark, cursor); bookmark.getAdvancedSettings().setRedirectSDCard(cursor.getInt(cursor.getColumnIndex("redirect_sdcard")) == 0 ? false : true); + bookmark.getAdvancedSettings().setRedirectSound(cursor.getInt(cursor.getColumnIndex("redirect_sound"))); + bookmark.getAdvancedSettings().setRedirectMicrophone(cursor.getInt(cursor.getColumnIndex("redirect_microphone")) == 0 ? false : true); bookmark.getAdvancedSettings().setSecurity(cursor.getInt(cursor.getColumnIndex("security"))); bookmark.getAdvancedSettings().setConsoleMode(cursor.getInt(cursor.getColumnIndex("console_mode")) == 0 ? false : true); bookmark.getAdvancedSettings().setRemoteProgram(cursor.getString(cursor.getColumnIndex("remote_program"))); diff --git a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkDB.java b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkDB.java index e54cc1502..97bf6193b 100644 --- a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkDB.java +++ b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/BookmarkDB.java @@ -22,14 +22,14 @@ import android.database.sqlite.SQLiteOpenHelper; public class BookmarkDB extends SQLiteOpenHelper { - private static final int DB_VERSION = 3; + private static final int DB_VERSION = 4; private static final String DB_NAME = "bookmarks.db"; public static final String ID = BaseColumns._ID; - + public BookmarkDB(Context context) { - super(context, DB_NAME, null, DB_VERSION); + super(context, DB_NAME, null, DB_VERSION); } @Override @@ -101,6 +101,8 @@ public class BookmarkDB extends SQLiteOpenHelper + "screen_3g, " + "performance_3g, " + "redirect_sdcard, " + + "redirect_sound, " + + "redirect_microphone, " + "security, " + "remote_program, " + "work_dir, " @@ -112,7 +114,7 @@ public class BookmarkDB extends SQLiteOpenHelper + "'', " + "'', " + "3389, " - + "1, 1, 2, 2, 0, 0, '', '', 0);"; + + "1, 1, 2, 2, 0, 0, 0, 0, '', '', 0);"; db.execSQL(sqlInsertDefaultSessionEntry); } @@ -140,7 +142,9 @@ public class BookmarkDB extends SQLiteOpenHelper + "enable_3g_settings INTEGER DEFAULT 0, " + "screen_3g INTEGER NOT NULL, " + "performance_3g INTEGER NOT NULL, " - + "redirect_sdcard INTEGER, " + + "redirect_sdcard INTEGER DEFAULT 0, " + + "redirect_sound INTEGER DEFAULT 0, " + + "redirect_microphone INTEGER DEFAULT 0, " + "security INTEGER, " + "remote_program TEXT, " + "work_dir TEXT, " diff --git a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/LibFreeRDP.java b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/LibFreeRDP.java index 54de217c9..32ce83d27 100644 --- a/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/LibFreeRDP.java +++ b/client/Android/FreeRDPCore/src/com/freerdp/freerdpcore/services/LibFreeRDP.java @@ -40,6 +40,8 @@ public class LibFreeRDP private static native void freerdp_set_data_directory(int inst, String directory); private static native void freerdp_set_clipboard_redirection(int inst, boolean enable); + private static native void freerdp_set_sound_redirection(int inst, int redirect); + private static native void freerdp_set_microphone_redirection(int inst, boolean enable); private static native void freerdp_set_drive_redirection(int inst, String path); private static native void freerdp_set_gateway_info(int inst, String gatewayhostname, int port, @@ -165,7 +167,15 @@ public class LibFreeRDP freerdp_set_gateway_info(inst, gatewaySettings.getHostname(), gatewaySettings.getPort(), gatewaySettings.getUsername(), gatewaySettings.getPassword(), gatewaySettings.getDomain()); } - + + // Sound redirection + freerdp_set_sound_redirection(inst, + advancedSettings.getRedirectSound()); + + // Microphone redirection + freerdp_set_microphone_redirection(inst, + advancedSettings.getRedirectMicrophone()); + return true; } diff --git a/client/Android/aFreeRDP/.classpath b/client/Android/aFreeRDP/.classpath index a4763d1ee..aacc761ec 100644 --- a/client/Android/aFreeRDP/.classpath +++ b/client/Android/aFreeRDP/.classpath @@ -1,8 +1,7 @@ - - - - + + + diff --git a/client/Android/aFreeRDP/.project b/client/Android/aFreeRDP/.project index 335291e03..0174cad38 100644 --- a/client/Android/aFreeRDP/.project +++ b/client/Android/aFreeRDP/.project @@ -3,6 +3,7 @@ aFreeRDP + FreeRDPCore diff --git a/client/Android/aFreeRDP/CMakeLists.txt b/client/Android/aFreeRDP/CMakeLists.txt index f4261b2ad..b335a6f99 100644 --- a/client/Android/aFreeRDP/CMakeLists.txt +++ b/client/Android/aFreeRDP/CMakeLists.txt @@ -16,7 +16,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(ANDROID_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(ANDROID_PACKAGE_NAME "aFreeRDP") CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake @@ -36,6 +35,32 @@ if (ANDROID_SDK) ${CMAKE_CURRENT_BINARY_DIR}/local.properties @ONLY) endif() +if("${ANDROID_DEBUG_ENABLE}" STREQUAL "true") + # 1. generate Android.mk + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/jni/Android.mk + "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n") + + # 2. generate gdb.setup + get_directory_property(INCLUDE_DIRECTORIES DIRECTORY . INCLUDE_DIRECTORIES) + string(REGEX REPLACE ";" " " INCLUDE_DIRECTORIES "${INCLUDE_DIRECTORIES}") + set(LIB_DIRECTORIES "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}") + file(WRITE ${ANDROID_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup + "set solib-search-path ${CMAKE_CURRENT_BINARY_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}\n") + file(APPEND ${ANDROID_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}/gdb.setup + "directory ${INCLUDE_DIRECTORIES} ${LIB_DIRECTORIES}\n") + + # 3. copy gdbserver executable + file(COPY ${ANDROID_NDK}/prebuilt/android-${ANDROID_ARCH_NAME}/gdbserver/gdbserver + DESTINATION ${ANDROID_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}/) + + # 4. Convenience target to launch debugger. + add_custom_target(debug-ndk + COMMAND adb install -r bin/aFreeRDP-debug.apk + COMMAND ndk-gdb --start --verbose --force + WORKING_DIRECTORY ${CURRENT_BINARY_DIR} + ) +endif() + if(ANDROID_BUILD_JAVA) if(NOT ANDROID_BUILD_JAVA_DEBUG) set(APK "${ANDROID_BINARY_DIR}/bin/${ANDROID_PACKAGE_NAME}-release-unsigned.apk") diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index c40e93a0a..915d114d4 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -12,6 +12,7 @@ endif() option(WITH_MANPAGES "Generate manpages." ON) option(WITH_PROFILER "Compile profiler." OFF) +option(WITH_GPROF "Compile with GProf profiler." OFF) if((TARGET_ARCH MATCHES "x86|x64") AND (NOT DEFINED WITH_SSE2)) option(WITH_SSE2 "Enable SSE2 optimization." ON) @@ -106,6 +107,7 @@ option(WITH_DEBUG_RDP "Print RDP debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_REDIR "Redirection debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_RFX "Print RemoteFX debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_SCARD "Print smartcard debug messages" ${DEFAULT_DEBUG_OPTION}) +option(WITH_DEBUG_SND "Print rdpsnd debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_SVC "Print static virtual channel debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_TRANSPORT "Print transport debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_TIMEZONE "Print timezone debug messages." ${DEFAULT_DEBUG_OPTION}) diff --git a/cmake/ConfigOptionsAndroid.cmake b/cmake/ConfigOptionsAndroid.cmake index aa00fac5b..4ffec393d 100644 --- a/cmake/ConfigOptionsAndroid.cmake +++ b/cmake/ConfigOptionsAndroid.cmake @@ -21,10 +21,11 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") endif() option(WITH_DEBUG_ANDROID_JNI "Enable debug output for android jni bindings" ${DEFAULT_DEBUG_OPTION}) +option(WITH_OPENSLES "Enable sound and microphone redirection using OpenSLES" ON) option(ANDROID_BUILD_JAVA "Automatically android java code - build type depends on CMAKE_BUILD_TYPE" ON) option(ANDROID_BUILD_JAVA_DEBUG "Create a android debug package" ${JAVA_DEBUG_DEFAULT}) set(ANDROID_APP_TARGET_SDK 11 CACHE STRING "Application target android SDK") -set(ANDROID_APP_MIN_SDK 8 CACHE STRING "Application minimum android SDK requirement") +set(ANDROID_APP_MIN_SDK 9 CACHE STRING "Application minimum android SDK requirement") set(ANDROID_APP_GOOGLE_TARGET_SDK "16" CACHE STRING "Application target google SDK") diff --git a/cmake/FindOpenSLES.cmake b/cmake/FindOpenSLES.cmake new file mode 100644 index 000000000..d11a64324 --- /dev/null +++ b/cmake/FindOpenSLES.cmake @@ -0,0 +1,34 @@ +# - Find OpenSLES +# Find the OpenSLES includes and libraries +# +# OPENSLES_INCLUDE_DIR - where to find dsound.h +# OPENSLES_LIBRARIES - List of libraries when using dsound. +# OPENSLES_FOUND - True if dsound found. + +if(OPENSLES_INCLUDE_DIR) + # Already in cache, be silent + set(OPENSLES_FIND_QUIETLY TRUE) +else() + find_package(PkgConfig) + pkg_check_modules(PC_OPENSLES QUIET OpenSLES) +endif(OPENSLES_INCLUDE_DIR) + +find_path(OPENSLES_INCLUDE_DIR SLES/OpenSLES.h + HINTS ${PC_OPENSLES_INCLUDE_DIR}) + +find_library(OPENSLES_LIBRARY NAMES OpenSLES + HINTS ${PC_OPENSLES_LIBDIR} ${PC_OPENSLES_LIBARRY_DIRS}) + +# Handle the QUIETLY and REQUIRED arguments and set OPENSL_FOUND to TRUE if +# all listed variables are TRUE. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(OPENSLES DEFAULT_MSG + OPENSLES_INCLUDE_DIR OPENSLES_LIBRARY) + +if(OPENSLES_FOUND) + set(OPENSLES_LIBRARIES ${OPENSLES_LIBRARY}) +else(OPENSLES_FOUND) + set(OPENSLES_LIBRARIES) +endif(OPENSLES_FOUND) + +mark_as_advanced(OPENSLES_INCLUDE_DIR OPENSLES_LIBRARY) diff --git a/config.h.in b/config.h.in index bbbe36102..d1eac49c1 100755 --- a/config.h.in +++ b/config.h.in @@ -37,6 +37,7 @@ /* Options */ #cmakedefine WITH_PROFILER +#cmakedefine WITH_GPROF #cmakedefine WITH_SSE2 #cmakedefine WITH_NEON #cmakedefine WITH_IPP @@ -45,6 +46,13 @@ #cmakedefine WITH_WIN8 #cmakedefine WITH_RDPSND_DSOUND +#cmakedefine WITH_WINMM +#cmakedefine WITH_MACAUDIO +#cmakedefine WITH_ALSA +#cmakedefine WITH_PULSE +#cmakedefine WITH_IOSAUDIO +#cmakedefine WITH_OPENSLES + /* Plugins */ #cmakedefine STATIC_CHANNELS #cmakedefine WITH_RDPDR @@ -69,6 +77,7 @@ #cmakedefine WITH_DEBUG_REDIR #cmakedefine WITH_DEBUG_RFX #cmakedefine WITH_DEBUG_SCARD +#cmakedefine WITH_DEBUG_SND #cmakedefine WITH_DEBUG_SVC #cmakedefine WITH_DEBUG_RDPEI #cmakedefine WITH_DEBUG_TIMEZONE diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index 3a167bcf5..1dbdb2ce1 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -52,12 +52,13 @@ if(MONOLITHIC_BUILD) set(${MODULE_PREFIX}_OBJECTS ${${MODULE_PREFIX}_OBJECTS} "$") endforeach() - add_library(${MODULE_NAME} dummy.c ${${MODULE_PREFIX}_OBJECTS}) + add_library(${MODULE_NAME} dummy.c + ${${MODULE_PREFIX}_OBJECTS}) set_target_properties(${MODULE_NAME} PROPERTIES LINKER_LANGUAGE C) set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVERSION ${FREERDP_API_VERSION} PREFIX "lib") - target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} ${PROFILER_LIBRARIES}) install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries) diff --git a/scripts/android_setup_build_env.sh b/scripts/android_setup_build_env.sh new file mode 100755 index 000000000..d96f5de72 --- /dev/null +++ b/scripts/android_setup_build_env.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# This script checks out or updates and builds third party libraries +# required for the android build. +# +# Specifically these are: +# - OpenSSL +# - Android NDK Profiler +# +# Usage: +# android_setup_build_env.sh + +OPENSSL_SCM=https://github.com/bmiklautz/android-external-openssl-ndk-static +NDK_PROFILER_SCM=https://github.com/richq/android-ndk-profiler + +SCRIPT_NAME=`basename $0` + +if [ $# -ne 1 ]; then + + echo "Missing command line argument." + echo "$SCRIPT_NAME " + exit -1 +fi + +if [ ! -d $1 ]; then + echo "Argument '$1' is not a directory." + exit -2 +fi +SRC=`realpath $1` + +echo "Using '$SRC' as root." + +echo "Preparing OpenSSL..." +OPENSSL_SRC=$SRC/external/openssl +if [ -d $OPENSSL_SRC ]; then + cd $OPENSSL_SRC + git pull + RETVAL=$? +else + git clone $OPENSSL_SCM $OPENSSL_SRC + RETVAL=$? + cd $OPENSSL_SRC +fi +if [ $RETVAL -ne 0 ]; then + echo "Failed to execute git command [$RETVAL]" + exit -3 +fi +ndk-build +RETVAL=$? +if [ $RETVAL -ne 0 ]; then + echo "Failed to execute ndk-build command [$RETVAL]" + exit -4 +fi + +echo "Preparing NDK profiler..." +NDK_PROFILER_SRC=$SRC/external/android-ndk-profiler +if [ -d $NDK_PROFILER_SRC ]; then + cd $NDK_PROFILER_SRC + git pull + RETVAL=$? +else + git clone $NDK_PROFILER_SCM $NDK_PROFILER_SRC + RETVAL=$? + cd $NDK_PROFILER_SRC +fi +if [ $RETVAL -ne 0 ]; then + echo "Failed to execute git command [$RETVAL]" + exit -5 +fi +ndk-build +RETVAL=$? +if [ $RETVAL -ne 0 ]; then + echo "Failed to execute ndk-build command [$RETVAL]" + exit -6 +fi + +echo "Prepared external libraries, you can now build the application." +exit 0 diff --git a/winpr/include/winpr/wlog.h b/winpr/include/winpr/wlog.h index 394e1a9c6..775d196dd 100644 --- a/winpr/include/winpr/wlog.h +++ b/winpr/include/winpr/wlog.h @@ -88,6 +88,7 @@ typedef int (*WLOG_APPENDER_WRITE_MESSAGE_FN)(wLog* log, wLogAppender* appender, #define WLOG_APPENDER_COMMON() \ DWORD Type; \ + DWORD State; \ wLogLayout* Layout; \ WLOG_APPENDER_OPEN_FN Open; \ WLOG_APPENDER_CLOSE_FN Close; \ @@ -114,6 +115,8 @@ struct _wLogFileAppender WLOG_APPENDER_COMMON(); char* FileName; + char* FilePath; + char* FullFileName; FILE* FileDescriptor; }; typedef struct _wLogFileAppender wLogFileAppender; @@ -127,7 +130,15 @@ struct _wLog LPSTR Name; DWORD Level; + BOOL IsRoot; + LPSTR* Names; + DWORD NameCount; wLogAppender* Appender; + + wLog* Parent; + wLog** Children; + DWORD ChildrenCount; + DWORD ChildrenSize; }; WINPR_API void WLog_PrintMessage(wLog* log, wLogMessage* message, ...); @@ -156,11 +167,15 @@ WINPR_API int WLog_CloseAppender(wLog* log); WINPR_API void WLog_ConsoleAppender_SetOutputStream(wLog* log, wLogConsoleAppender* appender, int outputStream); WINPR_API void WLog_FileAppender_SetOutputFileName(wLog* log, wLogFileAppender* appender, const char* filename); +WINPR_API void WLog_FileAppender_SetOutputFilePath(wLog* log, wLogFileAppender* appender, const char* filepath); WINPR_API wLogLayout* WLog_GetLogLayout(wLog* log); WINPR_API void WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format); -WINPR_API wLog* WLog_New(LPCSTR name); -WINPR_API void WLog_Free(wLog* log); +WINPR_API wLog* WLog_GetRoot(); +WINPR_API wLog* WLog_Get(LPCSTR name); + +WINPR_API void WLog_Init(); +WINPR_API void WLog_Uninit(); #endif /* WINPR_WLOG_H */ diff --git a/winpr/libwinpr/credui/test/TestCredUIConfirmCredentials.c b/winpr/libwinpr/credui/test/TestCredUIConfirmCredentials.c index 02e8fc547..7d3b5b418 100644 --- a/winpr/libwinpr/credui/test/TestCredUIConfirmCredentials.c +++ b/winpr/libwinpr/credui/test/TestCredUIConfirmCredentials.c @@ -7,7 +7,6 @@ int TestCredUIConfirmCredentials(int argc, char* argv[]) { - return 0; } diff --git a/winpr/libwinpr/file/CMakeLists.txt b/winpr/libwinpr/file/CMakeLists.txt index 9014f4cda..38a72d797 100644 --- a/winpr/libwinpr/file/CMakeLists.txt +++ b/winpr/libwinpr/file/CMakeLists.txt @@ -31,7 +31,7 @@ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SO set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL MODULE winpr - MODULES winpr-crt winpr-handle winpr-path winpr-synch) + MODULES winpr-crt winpr-handle winpr-path winpr-error winpr-synch) if(MONOLITHIC_BUILD) diff --git a/winpr/libwinpr/file/file.c b/winpr/libwinpr/file/file.c index 022f6138a..2573ec150 100644 --- a/winpr/libwinpr/file/file.c +++ b/winpr/libwinpr/file/file.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -292,6 +293,17 @@ BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, status = read(pipe->clientfd, lpBuffer, nNumberOfBytesToRead); + if (status == 0) + { + switch (errno) + { + case ECONNRESET: + SetLastError(ERROR_BROKEN_PIPE); + status = -1; + break; + } + } + if (status < 0) { *lpNumberOfBytesRead = 0; diff --git a/winpr/libwinpr/pipe/pipe.c b/winpr/libwinpr/pipe/pipe.c index 83f15d6e1..583a373e7 100644 --- a/winpr/libwinpr/pipe/pipe.c +++ b/winpr/libwinpr/pipe/pipe.c @@ -131,12 +131,21 @@ HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD free(lpPipePath); + if (PathFileExistsA(pNamedPipe->lpFilePath)) + { + DeleteFileA(pNamedPipe->lpFilePath); + } + pNamedPipe->clientfd = -1; - pNamedPipe->serverfd = socket(PF_LOCAL, SOCK_STREAM, 0); pNamedPipe->ServerMode = TRUE; - if (PathFileExistsA(pNamedPipe->lpFilePath)) - DeleteFileA(pNamedPipe->lpFilePath); + pNamedPipe->serverfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (pNamedPipe->serverfd == -1) + { + fprintf(stderr, "CreateNamedPipeA: socket error\n"); + return NULL; + } ZeroMemory(&s, sizeof(struct sockaddr_un)); s.sun_family = AF_UNIX; @@ -144,16 +153,22 @@ HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD status = bind(pNamedPipe->serverfd, (struct sockaddr*) &s, sizeof(struct sockaddr_un)); - if (status == 0) + if (status != 0) { - status = listen(pNamedPipe->serverfd, 2); - - if (status == 0) - { - UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF); - } + fprintf(stderr, "CreateNamedPipeA: bind error\n"); + return NULL; } + status = listen(pNamedPipe->serverfd, 2); + + if (status != 0) + { + fprintf(stderr, "CreateNamedPipeA: listen error\n"); + return NULL; + } + + UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF); + return hNamedPipe; } @@ -183,7 +198,10 @@ BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped) status = accept(pNamedPipe->serverfd, (struct sockaddr*) &s, &length); if (status < 0) + { + fprintf(stderr, "ConnectNamedPipe: accept error\n"); return FALSE; + } pNamedPipe->clientfd = status; pNamedPipe->ServerMode = FALSE; diff --git a/winpr/libwinpr/thread/process.c b/winpr/libwinpr/thread/process.c index 62d7f1faf..cc58e3667 100644 --- a/winpr/libwinpr/thread/process.c +++ b/winpr/libwinpr/thread/process.c @@ -24,6 +24,7 @@ #include #include +#include /** * CreateProcessA @@ -222,6 +223,19 @@ BOOL _CreateProcessExA(HANDLE hToken, DWORD dwLogonFlags, if (pid == 0) { /* child process */ +#ifdef __sun + closefrom(3); +#else + int maxfd; +#ifdef F_MAXFD // on some BSD derivates + maxfd = fcntl(0, F_MAXFD); +#else + maxfd = sysconf(_SC_OPEN_MAX); +#endif + int fd; + for(fd=3; fd #include +#include #include int TestWLog(int argc, char* argv[]) { - wLog* log; + wLog* root; + wLog* logA; + wLog* logB; wLogLayout* layout; wLogAppender* appender; - log = WLog_New("CONSOLE_LOG_TEST"); + WLog_Init(); - WLog_SetLogLevel(log, WLOG_INFO); + root = WLog_GetRoot(); - WLog_SetLogAppenderType(log, WLOG_APPENDER_CONSOLE); - appender = WLog_GetLogAppender(log); + WLog_SetLogAppenderType(root, WLOG_APPENDER_CONSOLE); + appender = WLog_GetLogAppender(root); - layout = WLog_GetLogLayout(log); - WLog_Layout_SetPrefixFormat(log, layout, "[%lv:%mn] [%fl|%fn|%ln] - "); + layout = WLog_GetLogLayout(root); + WLog_Layout_SetPrefixFormat(root, layout, "[%lv:%mn] [%fl|%fn|%ln] - "); - WLog_ConsoleAppender_SetOutputStream(log, (wLogConsoleAppender*) appender, WLOG_CONSOLE_STDERR); - WLog_OpenAppender(log); + WLog_ConsoleAppender_SetOutputStream(root, (wLogConsoleAppender*) appender, WLOG_CONSOLE_STDERR); + WLog_OpenAppender(root); - WLog_Print(log, WLOG_INFO, "this is a test"); - WLog_Print(log, WLOG_WARN, "this is a %dnd %s", 2, "test"); - WLog_Print(log, WLOG_ERROR, "this is an error"); - WLog_Print(log, WLOG_TRACE, "this is a trace output"); + logA = WLog_Get("com.test.ChannelA"); + logB = WLog_Get("com.test.ChannelB"); - WLog_CloseAppender(log); - WLog_Free(log); + WLog_SetLogLevel(logA, WLOG_INFO); + WLog_SetLogLevel(logB, WLOG_ERROR); - log = WLog_New("FILE_LOG_TEST"); + WLog_Print(logA, WLOG_INFO, "this is a test"); + WLog_Print(logA, WLOG_WARN, "this is a %dnd %s", 2, "test"); + WLog_Print(logA, WLOG_ERROR, "this is an error"); + WLog_Print(logA, WLOG_TRACE, "this is a trace output"); - WLog_SetLogLevel(log, WLOG_WARN); + WLog_Print(logB, WLOG_INFO, "just some info"); + WLog_Print(logB, WLOG_WARN, "we're warning a %dnd %s", 2, "time"); + WLog_Print(logB, WLOG_ERROR, "we've got an error"); + WLog_Print(logB, WLOG_TRACE, "leaving a trace behind"); - WLog_SetLogAppenderType(log, WLOG_APPENDER_FILE); - appender = WLog_GetLogAppender(log); + WLog_CloseAppender(root); - layout = WLog_GetLogLayout(log); - WLog_Layout_SetPrefixFormat(log, layout, "[%lv:%mn] [%fl|%fn|%ln] - "); - - WLog_FileAppender_SetOutputFileName(log, (wLogFileAppender*) appender, "/tmp/wlog_test.log"); - WLog_OpenAppender(log); - - WLog_Print(log, WLOG_INFO, "this is a test"); - WLog_Print(log, WLOG_WARN, "this is a %dnd %s", 2, "test"); - WLog_Print(log, WLOG_ERROR, "this is an error"); - WLog_Print(log, WLOG_TRACE, "this is a trace output"); - - WLog_CloseAppender(log); - WLog_Free(log); + WLog_Uninit(); return 0; } diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.c b/winpr/libwinpr/utils/wlog/ConsoleAppender.c new file mode 100644 index 000000000..bc5f14dd8 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.c @@ -0,0 +1,107 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * 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 "wlog/ConsoleAppender.h" + +/** + * Console Appender + */ + +void WLog_ConsoleAppender_SetOutputStream(wLog* log, wLogConsoleAppender* appender, int outputStream) +{ + if (!appender) + return; + + if (appender->Type != WLOG_APPENDER_CONSOLE) + return; + + if (outputStream < 0) + outputStream = WLOG_CONSOLE_STDOUT; + + if (outputStream == WLOG_CONSOLE_STDOUT) + appender->outputStream = WLOG_CONSOLE_STDOUT; + else if (outputStream == WLOG_CONSOLE_STDERR) + appender->outputStream = WLOG_CONSOLE_STDERR; + else + appender->outputStream = WLOG_CONSOLE_STDOUT; +} + +int WLog_ConsoleAppender_Open(wLog* log, wLogConsoleAppender* appender) +{ + return 0; +} + +int WLog_ConsoleAppender_Close(wLog* log, wLogConsoleAppender* appender) +{ + return 0; +} + +int WLog_ConsoleAppender_WriteMessage(wLog* log, wLogConsoleAppender* appender, wLogMessage* message) +{ + FILE* fp; + char prefix[WLOG_MAX_PREFIX_SIZE]; + + if (message->Level > log->Level) + return 0; + + fp = (appender->outputStream == WLOG_CONSOLE_STDERR) ? stderr : stdout; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); + + return 1; +} + +wLogConsoleAppender* WLog_ConsoleAppender_New(wLog* log) +{ + wLogConsoleAppender* ConsoleAppender; + + ConsoleAppender = (wLogConsoleAppender*) malloc(sizeof(wLogConsoleAppender)); + + if (ConsoleAppender) + { + ZeroMemory(ConsoleAppender, sizeof(wLogConsoleAppender)); + + ConsoleAppender->Open = (WLOG_APPENDER_OPEN_FN) WLog_ConsoleAppender_Open; + ConsoleAppender->Close = (WLOG_APPENDER_OPEN_FN) WLog_ConsoleAppender_Close; + ConsoleAppender->WriteMessage = (WLOG_APPENDER_WRITE_MESSAGE_FN) WLog_ConsoleAppender_WriteMessage; + + ConsoleAppender->outputStream = WLOG_CONSOLE_STDOUT; + } + + return ConsoleAppender; +} + +void WLog_ConsoleAppender_Free(wLog* log, wLogConsoleAppender* appender) +{ + if (appender) + { + free(appender); + } +} diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.h b/winpr/libwinpr/utils/wlog/ConsoleAppender.h new file mode 100644 index 000000000..e5871e847 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.h @@ -0,0 +1,30 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * 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. + */ + +#ifndef WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H +#define WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H + +#include + +#include "wlog/wlog.h" + +WINPR_API wLogConsoleAppender* WLog_ConsoleAppender_New(wLog* log); +WINPR_API void WLog_ConsoleAppender_Free(wLog* log, wLogConsoleAppender* appender); + +#endif /* WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog/FileAppender.c b/winpr/libwinpr/utils/wlog/FileAppender.c new file mode 100644 index 000000000..14388aeb1 --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.c @@ -0,0 +1,171 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * 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 "wlog/FileAppender.h" + +/** + * File Appender + */ + +void WLog_FileAppender_SetOutputFileName(wLog* log, wLogFileAppender* appender, const char* filename) +{ + if (!appender) + return; + + if (appender->Type != WLOG_APPENDER_FILE) + return; + + if (!filename) + return; + + appender->FileName = _strdup(filename); +} + +void WLog_FileAppender_SetOutputFilePath(wLog* log, wLogFileAppender* appender, const char* filepath) +{ + if (!appender) + return; + + if (appender->Type != WLOG_APPENDER_FILE) + return; + + if (!filepath) + return; + + appender->FilePath = _strdup(filepath); +} + +int WLog_FileAppender_Open(wLog* log, wLogFileAppender* appender) +{ + DWORD ProcessId; + + ProcessId = GetCurrentProcessId(); + + if (!appender->FilePath) + { + appender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog"); + } + + if (!PathFileExistsA(appender->FilePath)) + { + CreateDirectoryA(appender->FilePath, 0); + UnixChangeFileMode(appender->FilePath, 0xFFFF); + } + + if (!appender->FileName) + { + appender->FileName = (char*) malloc(256); + sprintf_s(appender->FileName, 256, "%u.log", ProcessId); + } + + if (!appender->FullFileName) + { + appender->FullFileName = GetCombinedPath(appender->FilePath, appender->FileName); + } + + appender->FileDescriptor = fopen(appender->FullFileName, "a+"); + + if (!appender->FileDescriptor) + return -1; + + return 0; +} + +int WLog_FileAppender_Close(wLog* log, wLogFileAppender* appender) +{ + if (!appender->FileDescriptor) + return 0; + + fclose(appender->FileDescriptor); + + appender->FileDescriptor = NULL; + + return 0; +} + +int WLog_FileAppender_WriteMessage(wLog* log, wLogFileAppender* appender, wLogMessage* message) +{ + FILE* fp; + char prefix[WLOG_MAX_PREFIX_SIZE]; + + if (message->Level > log->Level) + return 0; + + fp = appender->FileDescriptor; + + if (!fp) + return -1; + + message->PrefixString = prefix; + WLog_Layout_GetMessagePrefix(log, appender->Layout, message); + + fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); + + return 1; +} + +wLogFileAppender* WLog_FileAppender_New(wLog* log) +{ + wLogFileAppender* FileAppender; + + FileAppender = (wLogFileAppender*) malloc(sizeof(wLogFileAppender)); + + if (FileAppender) + { + ZeroMemory(FileAppender, sizeof(wLogFileAppender)); + + FileAppender->Open = (WLOG_APPENDER_OPEN_FN) WLog_FileAppender_Open; + FileAppender->Close = (WLOG_APPENDER_OPEN_FN) WLog_FileAppender_Close; + FileAppender->WriteMessage = (WLOG_APPENDER_WRITE_MESSAGE_FN) WLog_FileAppender_WriteMessage; + + FileAppender->FileName = NULL; + FileAppender->FilePath = NULL; + FileAppender->FullFileName = NULL; + } + + return FileAppender; +} + +void WLog_FileAppender_Free(wLog* log, wLogFileAppender* appender) +{ + if (appender) + { + if (appender->FileName) + free(appender->FileName); + + if (appender->FilePath) + free(appender->FilePath); + + if (appender->FullFileName) + free(appender->FullFileName); + + free(appender); + } +} diff --git a/winpr/libwinpr/utils/wlog/FileAppender.h b/winpr/libwinpr/utils/wlog/FileAppender.h new file mode 100644 index 000000000..de51249df --- /dev/null +++ b/winpr/libwinpr/utils/wlog/FileAppender.h @@ -0,0 +1,30 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * 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. + */ + +#ifndef WINPR_WLOG_FILE_APPENDER_PRIVATE_H +#define WINPR_WLOG_FILE_APPENDER_PRIVATE_H + +#include + +#include "wlog/wlog.h" + +WINPR_API wLogFileAppender* WLog_FileAppender_New(wLog* log); +WINPR_API void WLog_FileAppender_Free(wLog* log, wLogFileAppender* appender); + +#endif /* WINPR_WLOG_FILE_APPENDER_PRIVATE_H */ diff --git a/winpr/libwinpr/utils/wlog.c b/winpr/libwinpr/utils/wlog/wlog.c similarity index 69% rename from winpr/libwinpr/utils/wlog.c rename to winpr/libwinpr/utils/wlog/wlog.c index aced199d8..83c12b1a8 100644 --- a/winpr/libwinpr/utils/wlog.c +++ b/winpr/libwinpr/utils/wlog/wlog.c @@ -30,6 +30,11 @@ #include +#include "wlog/wlog.h" + +#include "wlog/FileAppender.h" +#include "wlog/ConsoleAppender.h" + /** * References for general logging concepts: * @@ -40,9 +45,6 @@ * http://docs.python.org/2/library/logging.html */ -#define WLOG_MAX_PREFIX_SIZE 512 -#define WLOG_MAX_STRING_SIZE 8192 - const char* WLOG_LEVELS[7] = { "TRACE", @@ -56,13 +58,17 @@ const char* WLOG_LEVELS[7] = int WLog_Write(wLog* log, wLogMessage* message) { - if (!log->Appender) + wLogAppender* appender; + + appender = WLog_GetLogAppender(log); + + if (!appender) return -1; - if (!log->Appender->WriteMessage) + if (!appender->WriteMessage) return -1; - return log->Appender->WriteMessage(log, log->Appender, message); + return appender->WriteMessage(log, appender, message); } void WLog_PrintMessageVA(wLog* log, wLogMessage* message, va_list args) @@ -74,7 +80,7 @@ void WLog_PrintMessageVA(wLog* log, wLogMessage* message, va_list args) } else { - char formattedLogMessage[8192]; + char formattedLogMessage[WLOG_MAX_STRING_SIZE]; wvsnprintfx(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message->FormatString, args); message->TextString = formattedLogMessage; @@ -296,7 +302,11 @@ void WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* me wLogLayout* WLog_GetLogLayout(wLog* log) { - return log->Appender->Layout; + wLogAppender* appender; + + appender = WLog_GetLogAppender(log); + + return appender->Layout; } void WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format) @@ -334,172 +344,6 @@ void WLog_Layout_Free(wLog* log, wLogLayout* layout) } } -/** - * Console Appender - */ - -void WLog_ConsoleAppender_SetOutputStream(wLog* log, wLogConsoleAppender* appender, int outputStream) -{ - if (!appender) - return; - - if (outputStream < 0) - outputStream = WLOG_CONSOLE_STDOUT; - - if (outputStream == WLOG_CONSOLE_STDOUT) - appender->outputStream = WLOG_CONSOLE_STDOUT; - else if (outputStream == WLOG_CONSOLE_STDERR) - appender->outputStream = WLOG_CONSOLE_STDERR; - else - appender->outputStream = WLOG_CONSOLE_STDOUT; -} - -int WLog_ConsoleAppender_Open(wLog* log, wLogConsoleAppender* appender) -{ - return 0; -} - -int WLog_ConsoleAppender_Close(wLog* log, wLogConsoleAppender* appender) -{ - return 0; -} - -int WLog_ConsoleAppender_WriteMessage(wLog* log, wLogConsoleAppender* appender, wLogMessage* message) -{ - FILE* fp; - char prefix[WLOG_MAX_PREFIX_SIZE]; - - if (message->Level > log->Level) - return 0; - - fp = (appender->outputStream == WLOG_CONSOLE_STDERR) ? stderr : stdout; - - message->PrefixString = prefix; - WLog_Layout_GetMessagePrefix(log, appender->Layout, message); - - fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); - - return 1; -} - -wLogConsoleAppender* WLog_ConsoleAppender_New(wLog* log) -{ - wLogConsoleAppender* ConsoleAppender; - - ConsoleAppender = (wLogConsoleAppender*) malloc(sizeof(wLogConsoleAppender)); - - if (ConsoleAppender) - { - ZeroMemory(ConsoleAppender, sizeof(wLogConsoleAppender)); - - ConsoleAppender->Open = (WLOG_APPENDER_OPEN_FN) WLog_ConsoleAppender_Open; - ConsoleAppender->Close = (WLOG_APPENDER_OPEN_FN) WLog_ConsoleAppender_Close; - ConsoleAppender->WriteMessage = (WLOG_APPENDER_WRITE_MESSAGE_FN) WLog_ConsoleAppender_WriteMessage; - - ConsoleAppender->outputStream = WLOG_CONSOLE_STDOUT; - } - - return ConsoleAppender; -} - -void WLog_ConsoleAppender_Free(wLog* log, wLogConsoleAppender* appender) -{ - if (appender) - { - free(appender); - } -} - -/** - * File Appender - */ - -void WLog_FileAppender_SetOutputFileName(wLog* log, wLogFileAppender* appender, const char* filename) -{ - if (!appender) - return; - - if (!filename) - return; - - appender->FileName = _strdup(filename); -} - -int WLog_FileAppender_Open(wLog* log, wLogFileAppender* appender) -{ - if (!appender->FileName) - return -1; - - appender->FileDescriptor = fopen(appender->FileName, "a+"); - - if (!appender->FileDescriptor) - return -1; - - return 0; -} - -int WLog_FileAppender_Close(wLog* log, wLogFileAppender* appender) -{ - if (!appender->FileDescriptor) - return 0; - - fclose(appender->FileDescriptor); - - appender->FileDescriptor = NULL; - - return 0; -} - -int WLog_FileAppender_WriteMessage(wLog* log, wLogFileAppender* appender, wLogMessage* message) -{ - FILE* fp; - char prefix[WLOG_MAX_PREFIX_SIZE]; - - if (message->Level > log->Level) - return 0; - - fp = appender->FileDescriptor; - - if (!fp) - return -1; - - message->PrefixString = prefix; - WLog_Layout_GetMessagePrefix(log, appender->Layout, message); - - fprintf(fp, "%s%s\n", message->PrefixString, message->TextString); - - return 1; -} - -wLogFileAppender* WLog_FileAppender_New(wLog* log) -{ - wLogFileAppender* FileAppender; - - FileAppender = (wLogFileAppender*) malloc(sizeof(wLogFileAppender)); - - if (FileAppender) - { - ZeroMemory(FileAppender, sizeof(wLogFileAppender)); - - FileAppender->Open = (WLOG_APPENDER_OPEN_FN) WLog_FileAppender_Open; - FileAppender->Close = (WLOG_APPENDER_OPEN_FN) WLog_FileAppender_Close; - FileAppender->WriteMessage = (WLOG_APPENDER_WRITE_MESSAGE_FN) WLog_FileAppender_WriteMessage; - } - - return FileAppender; -} - -void WLog_FileAppender_Free(wLog* log, wLogFileAppender* appender) -{ - if (appender) - { - if (appender->FileName) - free(appender->FileName); - - free(appender); - } -} - wLogAppender* WLog_Appender_New(wLog* log, DWORD logAppenderType) { wLogAppender* appender = NULL; @@ -544,6 +388,12 @@ void WLog_Appender_Free(wLog* log, wLogAppender* appender) wLogAppender* WLog_GetLogAppender(wLog* log) { + if (!log) + return NULL; + + if (!log->Appender) + return WLog_GetLogAppender(log->Parent); + return log->Appender; } @@ -560,24 +410,80 @@ void WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType) int WLog_OpenAppender(wLog* log) { - if (!log->Appender) + int status = 0; + wLogAppender* appender; + + appender = WLog_GetLogAppender(log); + + if (!appender) return -1; - if (!log->Appender->Open) + if (!appender->Open) return 0; - return log->Appender->Open(log, log->Appender); + if (!appender->State) + { + status = appender->Open(log, appender); + appender->State = 1; + } + + return status; } int WLog_CloseAppender(wLog* log) { - if (!log->Appender) + int status = 0; + wLogAppender* appender; + + appender = WLog_GetLogAppender(log); + + if (!appender) return -1; - if (!log->Appender->Close) + if (!appender->Close) return 0; - return log->Appender->Close(log, log->Appender); + if (appender->State) + { + status = appender->Close(log, appender); + appender->State = 0; + } + + return status; +} + +int WLog_ParseName(wLog* log, LPCSTR name) +{ + char* p; + int count; + LPSTR names; + + count = 1; + p = (char*) name; + + while ((p = strchr(p, '.')) != NULL) + { + count++; + p++; + } + + names = _strdup(name); + log->NameCount = count; + log->Names = (LPSTR*) malloc(sizeof(LPSTR) * (count + 1)); + log->Names[count] = NULL; + + count = 0; + p = (char*) names; + log->Names[count++] = p; + + while ((p = strchr(p, '.')) != NULL) + { + log->Names[count++] = p + 1; + *p = '\0'; + p++; + } + + return 0; } wLog* WLog_New(LPCSTR name) @@ -591,9 +497,17 @@ wLog* WLog_New(LPCSTR name) ZeroMemory(log, sizeof(wLog)); log->Name = _strdup(name); + WLog_ParseName(log, name); + log->Level = WLOG_TRACE; - WLog_SetLogAppenderType(log, WLOG_APPENDER_CONSOLE); + log->Parent = NULL; + log->ChildrenCount = 0; + + log->ChildrenSize = 16; + log->Children = (wLog**) malloc(sizeof(wLog*) * log->ChildrenSize); + + log->Appender = NULL; } return log; @@ -610,6 +524,91 @@ void WLog_Free(wLog* log) } free(log->Name); + free(log->Names[0]); + free(log->Names); + free(log); } } + +static wLog* g_RootLog = NULL; + +wLog* WLog_GetRoot() +{ + if (!g_RootLog) + { + g_RootLog = WLog_New(""); + g_RootLog->IsRoot = TRUE; + WLog_SetLogAppenderType(g_RootLog, WLOG_APPENDER_CONSOLE); + } + + return g_RootLog; +} + +int WLog_AddChild(wLog* parent, wLog* child) +{ + if (parent->ChildrenCount >= parent->ChildrenSize) + { + parent->ChildrenSize *= 2; + parent->Children = (wLog**) realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize); + } + + parent->Children[parent->ChildrenCount++] = child; + child->Parent = parent; + + return 0; +} + +wLog* WLog_FindChild(LPCSTR name) +{ + int index; + wLog* root; + wLog* child = NULL; + BOOL found = FALSE; + + root = WLog_GetRoot(); + + for (index = 0; index < root->ChildrenCount; index++) + { + child = root->Children[index]; + + if (strcmp(child->Name, name) == 0) + { + found = TRUE; + break; + } + } + + return (found) ? child : NULL; +} + +wLog* WLog_Get(LPCSTR name) +{ + wLog* log; + wLog* root; + + root = WLog_GetRoot(); + + log = WLog_FindChild(name); + + if (!log) + { + log = WLog_New(name); + WLog_AddChild(root, log); + } + + return log; +} + +void WLog_Init() +{ + WLog_GetRoot(); +} + +void WLog_Uninit() +{ + wLog* root = WLog_GetRoot(); + + WLog_Free(root); + g_RootLog = NULL; +} diff --git a/winpr/libwinpr/utils/wlog/wlog.h b/winpr/libwinpr/utils/wlog/wlog.h new file mode 100644 index 000000000..7c2ce867a --- /dev/null +++ b/winpr/libwinpr/utils/wlog/wlog.h @@ -0,0 +1,30 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Logger + * + * Copyright 2013 Marc-Andre Moreau + * + * 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. + */ + +#ifndef WINPR_WLOG_PRIVATE_H +#define WINPR_WLOG_PRIVATE_H + +#include + +#define WLOG_MAX_PREFIX_SIZE 512 +#define WLOG_MAX_STRING_SIZE 8192 + +WINPR_API void WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message); + +#endif /* WINPR_WLOG_PRIVATE_H */