mirror of https://github.com/FreeRDP/FreeRDP
Merge branch 'master' of https://github.com/awakecoding/FreeRDP
This commit is contained in:
commit
afbebbcdf7
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Armin Novak <armin.novak@gmail.com>
|
||||
#
|
||||
# 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()
|
|
@ -0,0 +1,411 @@
|
|||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
|
||||
*
|
||||
* Copyright 2013 Armin Novak <armin.novak@gmail.com>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
|
||||
#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, "<device>",
|
||||
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;
|
||||
}
|
|
@ -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 <organization> 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 <COPYRIGHT HOLDER> 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 <assert.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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 <organization> 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 <COPYRIGHT HOLDER> 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 <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
|
@ -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, "<device>", 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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Armin Novak <armin.novak@gmail.com>
|
||||
#
|
||||
# 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()
|
|
@ -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 <organization> 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 <COPYRIGHT HOLDER> 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 <assert.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -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 <organization> 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 <COPYRIGHT HOLDER> 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 <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <stdlib.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#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
|
|
@ -0,0 +1,469 @@
|
|||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Output Virtual Channel
|
||||
*
|
||||
* Copyright 2013 Armin Novak <armin.novak@gmail.com>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/cmdline.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/types.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/utils/debug.h>
|
||||
|
||||
#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, "<device>",
|
||||
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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,12 @@
|
|||
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<supports-screens android:anyDensity="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" />
|
||||
|
||||
<application>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}/)
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -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));
|
||||
|
||||
|
@ -330,6 +337,9 @@ int android_freerdp_run(freerdp* instance)
|
|||
}
|
||||
}
|
||||
|
||||
if (freerdp_shall_disconnect(instance))
|
||||
break;
|
||||
|
||||
if (freerdp_check_fds(instance) != TRUE)
|
||||
{
|
||||
DEBUG_ANDROID("Failed to check FreeRDP file descriptor\n");
|
||||
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -115,6 +115,18 @@
|
|||
<string name="settings_enable_gateway_settings">Enable Gateway</string>
|
||||
<string name="settings_gateway_settings">Gateway Settings</string>
|
||||
<string name="settings_redirect_sdcard">Redirect SDCard</string>
|
||||
<string name="settings_redirect_sound">Redirect Sound</string>
|
||||
<string-array name="redirect_sound_array">
|
||||
<item>Do not play</item>
|
||||
<item>Play on Server</item>
|
||||
<item>Play on Device</item>
|
||||
</string-array>
|
||||
<string-array name="redirect_sound_values_array">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<string name="settings_redirect_microphone">Redirect Microphone</string>
|
||||
<string name="settings_security">Seguridad</string>
|
||||
<string-array name="security_array">
|
||||
<item>Automatico</item>
|
||||
|
|
|
@ -114,6 +114,18 @@
|
|||
<string name="settings_enable_gateway_settings">Enable Gateway</string>
|
||||
<string name="settings_gateway_settings">Gateway Settings</string>
|
||||
<string name="settings_redirect_sdcard">"Redirect SDCard"</string>
|
||||
<string name="settings_redirect_sound">Redirect Sound</string>
|
||||
<string-array name="redirect_sound_array">
|
||||
<item>Do not play</item>
|
||||
<item>Play on Server</item>
|
||||
<item>Play on Device</item>
|
||||
</string-array>
|
||||
<string-array name="redirect_sound_values_array">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<string name="settings_redirect_microphone">Redirect Microphone</string>
|
||||
<string name="settings_security">"Securité"</string>
|
||||
<string-array name="security_array">
|
||||
<item>"Automatique"</item>
|
||||
|
|
|
@ -115,6 +115,18 @@
|
|||
<string name="settings_enable_gateway_settings">Gateway inschakelen</string>
|
||||
<string name="settings_gateway_settings">Gateway instellingen</string>
|
||||
<string name="settings_redirect_sdcard">SDCard aankoppelen</string>
|
||||
<string name="settings_redirect_sound">Redirect Sound</string>
|
||||
<string-array name="redirect_sound_array">
|
||||
<item>Do not play</item>
|
||||
<item>Play on Server</item>
|
||||
<item>Play on Device</item>
|
||||
</string-array>
|
||||
<string-array name="redirect_sound_values_array">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<string name="settings_redirect_microphone">Redirect Microphone</string>
|
||||
<string name="settings_security">Beveiliging</string>
|
||||
<string-array name="security_array">
|
||||
<item>Automatisch</item>
|
||||
|
|
|
@ -112,6 +112,18 @@
|
|||
<string name="settings_enable_gateway_settings">Enable Gateway</string>
|
||||
<string name="settings_gateway_settings">Gateway Settings</string>
|
||||
<string name="settings_redirect_sdcard">Redirect SDCard</string>
|
||||
<string name="settings_redirect_sound">Redirect Sound</string>
|
||||
<string-array name="redirect_sound_array">
|
||||
<item>Do not play</item>
|
||||
<item>Play on Server</item>
|
||||
<item>Play on Device</item>
|
||||
</string-array>
|
||||
<string-array name="redirect_sound_values_array">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
<string name="settings_redirect_microphone">Redirect Microphone</string>
|
||||
<string name="settings_security">Security</string>
|
||||
<string-array name="security_array">
|
||||
<item>Automatic</item>
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
</PreferenceScreen>
|
||||
|
||||
<CheckBoxPreference android:key="bookmark.redirect_sdcard" android:title="@string/settings_redirect_sdcard"/>
|
||||
<com.freerdp.freerdpcore.utils.IntListPreference android:key="bookmark.redirect_sound" android:title="@string/settings_redirect_sound" android:entries="@array/redirect_sound_array" android:entryValues="@array/redirect_sound_values_array" />
|
||||
<CheckBoxPreference android:key="bookmark.redirect_microphone" android:title="@string/settings_redirect_microphone"/>
|
||||
<com.freerdp.freerdpcore.utils.IntListPreference android:key="bookmark.security" android:title="@string/settings_security" android:entries="@array/security_array" android:entryValues="@array/security_values_array" />
|
||||
<EditTextPreference android:key="bookmark.remote_program" android:title="@string/settings_remote_program" android:summary="notepad.exe"/>
|
||||
<EditTextPreference android:key="bookmark.work_dir" android:title="@string/settings_work_dir"/>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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", ""));
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -22,7 +22,7 @@ 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;
|
||||
|
@ -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, "
|
||||
|
|
|
@ -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,
|
||||
|
@ -166,6 +168,14 @@ public class LibFreeRDP
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<name>aFreeRDP</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>FreeRDPCore</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -52,12 +52,13 @@ if(MONOLITHIC_BUILD)
|
|||
set(${MODULE_PREFIX}_OBJECTS ${${MODULE_PREFIX}_OBJECTS} "$<TARGET_OBJECTS:${MODULE_NAME}-${${MODULE_PREFIX}_SUBMODULE}>")
|
||||
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)
|
||||
|
||||
|
|
|
@ -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 <source root>
|
||||
|
||||
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 <FreeRDP source>"
|
||||
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
|
|
@ -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 */
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
int TestCredUIConfirmCredentials(int argc, char* argv[])
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <winpr/crt.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/error.h>
|
||||
#include <winpr/handle.h>
|
||||
#include <winpr/platform.h>
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <winpr/handle.h>
|
||||
|
||||
#include <winpr/thread.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
/**
|
||||
* 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<maxfd; fd++)
|
||||
close(fd);
|
||||
#endif // __sun
|
||||
|
||||
if (token)
|
||||
{
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
set(MODULE_NAME "winpr-utils")
|
||||
set(MODULE_PREFIX "WINPR_UTILS")
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(${MODULE_PREFIX}_COLLECTIONS_SRCS
|
||||
collections/Queue.c
|
||||
collections/Stack.c
|
||||
|
@ -46,17 +48,25 @@ set(${MODULE_PREFIX}_TRIO_SRCS
|
|||
trio/triostr.c
|
||||
trio/triostr.h)
|
||||
|
||||
set(${MODULE_PREFIX}_WLOG_SRCS
|
||||
wlog/wlog.c
|
||||
wlog/wlog.h
|
||||
wlog/FileAppender.c
|
||||
wlog/FileAppender.h
|
||||
wlog/ConsoleAppender.c
|
||||
wlog/ConsoleAppender.h)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
sam.c
|
||||
ntlm.c
|
||||
print.c
|
||||
stream.c
|
||||
cmdline.c
|
||||
wlog.c)
|
||||
cmdline.c)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
${${MODULE_PREFIX}_COLLECTIONS_SRCS}
|
||||
${${MODULE_PREFIX}_TRIO_SRCS})
|
||||
${${MODULE_PREFIX}_TRIO_SRCS}
|
||||
${${MODULE_PREFIX}_WLOG_SRCS})
|
||||
|
||||
include_directories("trio")
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
|
@ -79,7 +89,7 @@ endif()
|
|||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL
|
||||
MODULE winpr
|
||||
MODULES winpr-crt winpr-synch winpr-sysinfo)
|
||||
MODULES winpr-crt winpr-file winpr-path winpr-synch winpr-sysinfo)
|
||||
|
||||
if(MONOLITHIC_BUILD)
|
||||
set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
|
||||
|
|
|
@ -27,7 +27,7 @@ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
|||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE winpr
|
||||
MODULES winpr-crt winpr-thread winpr-utils)
|
||||
MODULES winpr-crt winpr-path winpr-thread winpr-utils)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
|
|
@ -1,55 +1,49 @@
|
|||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* WinPR Logger
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/crt.h>
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* WinPR Logger
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/wlog.h>
|
||||
|
||||
#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 */
|
|
@ -0,0 +1,171 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* WinPR Logger
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/crt.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* WinPR Logger
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/wlog.h>
|
||||
|
||||
#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 */
|
|
@ -30,6 +30,11 @@
|
|||
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* WinPR Logger
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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 <winpr/wlog.h>
|
||||
|
||||
#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 */
|
Loading…
Reference in New Issue