merged upstream

This commit is contained in:
C-o-r-E 2013-02-28 16:45:47 -05:00
commit 88207eaf68
339 changed files with 7378 additions and 5481 deletions

1
.gitignore vendored
View File

@ -41,6 +41,7 @@ client/X11/xfreerdp.1
# Mac OS X
.DS_Store
*.xcodeproj/
DerivedData/
# Windows
*.vcxproj

View File

@ -209,7 +209,7 @@ if(ANDROID)
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()
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_ANDROID_EXTERNAL_SSL_PATH})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/libs/${ANDROID_ABI})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/libs/${ANDROID_ABI})
endif()
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)

View File

@ -31,8 +31,9 @@
#include <alsa/asoundlib.h>
#include <freerdp/addin.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/thread.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
@ -73,15 +74,12 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_hand
snd_strerror(error));
return FALSE;
}
snd_pcm_hw_params_any(capture_handle, hw_params);
snd_pcm_hw_params_set_access(capture_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(capture_handle, hw_params,
alsa->format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params,
&alsa->actual_rate, NULL);
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
&alsa->actual_channels);
snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(capture_handle, hw_params, alsa->format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate, NULL);
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &alsa->actual_channels);
snd_pcm_hw_params(capture_handle, hw_params);
snd_pcm_hw_params_free(hw_params);
snd_pcm_prepare(capture_handle);
@ -94,6 +92,7 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_hand
alsa->actual_rate, alsa->actual_channels,
alsa->target_rate, alsa->target_channels);
}
return TRUE;
}
@ -133,20 +132,25 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
break;
cframes = alsa->frames_per_packet - alsa->buffer_frames;
if (cframes > frames)
cframes = frames;
memcpy(alsa->buffer + alsa->buffer_frames * tbytes_per_frame,
src, cframes * tbytes_per_frame);
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src, cframes * tbytes_per_frame);
alsa->buffer_frames += cframes;
if (alsa->buffer_frames >= alsa->frames_per_packet)
{
if (alsa->wformat == 0x11)
if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
{
alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
alsa->target_channels, alsa->block_size);
encoded_data = alsa->dsp_context->adpcm_buffer;
encoded_size = alsa->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
alsa->buffer_frames * tbytes_per_frame, encoded_size);
}
@ -162,11 +166,16 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
frames = 0;
}
else
{
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
}
alsa->buffer_frames = 0;
if (!ret)
break;
}
src += cframes * tbytes_per_frame;
frames -= cframes;
}
@ -225,8 +234,10 @@ static void* audin_alsa_thread_func(void* arg)
} while (0);
free(buffer);
free(alsa->buffer);
alsa->buffer = NULL;
if (capture_handle)
snd_pcm_close(capture_handle);
@ -253,7 +264,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* forma
{
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
@ -263,7 +274,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* forma
}
break;
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
@ -272,6 +283,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* forma
}
break;
}
return FALSE;
}
@ -284,9 +296,10 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
alsa->actual_rate = format->nSamplesPerSec;
alsa->target_channels = format->nChannels;
alsa->actual_channels = format->nChannels;
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
@ -300,7 +313,7 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
}
break;
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_DVI_ADPCM:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
@ -310,6 +323,7 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
alsa->frames_per_packet);
break;
}
alsa->wformat = format->wFormatTag;
alsa->block_size = format->nBlockAlign;
}

View File

@ -32,7 +32,7 @@
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include "audin_main.h"

View File

@ -27,6 +27,6 @@ set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
MODULES freerdp-codec freerdp-utils)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")

View File

@ -27,7 +27,7 @@
#include <winpr/crt.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/thread.h>
#include <freerdp/utils/stream.h>
#include <freerdp/channels/wtsvc.h>
@ -134,8 +134,8 @@ static BOOL audin_server_recv_formats(audin_server* audin, STREAM* s, UINT32 len
if (audin->context.num_client_formats <= 0)
return FALSE;
audin->context.client_formats = malloc(audin->context.num_client_formats * sizeof(rdpsndFormat));
ZeroMemory(audin->context.client_formats, audin->context.num_client_formats * sizeof(rdpsndFormat));
audin->context.client_formats = malloc(audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
ZeroMemory(audin->context.client_formats, audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
for (i = 0; i < audin->context.num_client_formats; i++)
{
@ -206,7 +206,7 @@ static BOOL audin_server_recv_open_reply(audin_server* audin, STREAM* s, UINT32
static BOOL audin_server_recv_data(audin_server* audin, STREAM* s, UINT32 length)
{
rdpsndFormat* format;
AUDIO_FORMAT* format;
int sbytes_per_sample;
int sbytes_per_frame;
BYTE* src;
@ -284,7 +284,8 @@ static void* audin_server_thread_func(void* arg)
/* Wait for the client to confirm that the Audio Input dynamic channel is ready */
while (1)
{
freerdp_thread_wait(thread);
if (freerdp_thread_wait(thread) < 0)
break;
if (freerdp_thread_is_stopped(thread))
break;
@ -309,7 +310,8 @@ static void* audin_server_thread_func(void* arg)
while (ready)
{
freerdp_thread_wait(thread);
if (freerdp_thread_wait(thread) < 0)
break;
if (freerdp_thread_is_stopped(thread))
break;

View File

@ -44,7 +44,7 @@
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/library.h>
#include <winpr/interlocked.h>
#include <winpr/collections.h>
#ifdef WITH_DEBUG_CHANNELS
#define DEBUG_CHANNELS(fmt, ...) DEBUG_CLASS(CHANNELS, fmt, ## __VA_ARGS__)
@ -383,15 +383,14 @@ struct channel_data
PCHANNEL_OPEN_EVENT_FN open_event_proc;
};
struct _SYNC_DATA
struct _CHANNEL_OPEN_EVENT
{
SLIST_ENTRY ItemEntry;
void* Data;
UINT32 DataLength;
void* UserData;
int Index;
};
typedef struct _SYNC_DATA SYNC_DATA;
typedef struct _CHANNEL_OPEN_EVENT CHANNEL_OPEN_EVENT;
typedef struct rdp_init_handle rdpInitHandle;
@ -430,15 +429,7 @@ struct rdp_channels
/* used for locating the channels for a given instance */
freerdp* instance;
/* signal for incoming data or event */
HANDLE signal;
/* used for sync write */
PSLIST_HEADER pSyncDataList;
/* used for sync event */
HANDLE event_sem;
RDP_EVENT* event;
wMessagePipe* MsgPipe;
};
/**
@ -505,6 +496,7 @@ static rdpChannels* freerdp_channels_find_by_instance(freerdp* instance)
for (channels_list = g_channels_list; channels_list; channels_list = channels_list->next)
{
channels = channels_list->channels;
if (channels->instance == instance)
{
ReleaseMutex(g_mutex_list);
@ -605,7 +597,7 @@ static UINT32 FREERDP_CC MyVirtualChannelInit(void** ppInitHandle, PCHANNEL_DEF
PCHANNEL_DEF lchannel_def;
struct channel_data* lchannel_data;
if (ppInitHandle == NULL)
if (!ppInitHandle)
{
DEBUG_CHANNELS("error bad init handle");
return CHANNEL_RC_BAD_INIT_HANDLE;
@ -630,7 +622,7 @@ static UINT32 FREERDP_CC MyVirtualChannelInit(void** ppInitHandle, PCHANNEL_DEF
return CHANNEL_RC_TOO_MANY_CHANNELS;
}
if (pChannel == 0)
if (!pChannel)
{
DEBUG_CHANNELS("error bad channel");
return CHANNEL_RC_BAD_CHANNEL;
@ -709,13 +701,13 @@ static UINT32 FREERDP_CC MyVirtualChannelOpen(void* pInitHandle, UINT32* pOpenHa
channels = ((rdpInitHandle*) pInitHandle)->channels;
if (pOpenHandle == 0)
if (!pOpenHandle)
{
DEBUG_CHANNELS("error bad channel handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
}
if (pChannelOpenEventProc == 0)
if (!pChannelOpenEventProc)
{
DEBUG_CHANNELS("error bad proc");
return CHANNEL_RC_BAD_PROC;
@ -729,7 +721,7 @@ static UINT32 FREERDP_CC MyVirtualChannelOpen(void* pInitHandle, UINT32* pOpenHa
lchannel_data = freerdp_channels_find_channel_data_by_name(channels, pChannelName, &index);
if (lchannel_data == 0)
if (!lchannel_data)
{
DEBUG_CHANNELS("error channel name");
return CHANNEL_RC_UNKNOWN_CHANNEL_NAME;
@ -785,13 +777,13 @@ static UINT32 FREERDP_CC MyVirtualChannelClose(UINT32 openHandle)
static UINT32 FREERDP_CC MyVirtualChannelWrite(UINT32 openHandle, void* pData, UINT32 dataLength, void* pUserData)
{
int index;
SYNC_DATA* item;
CHANNEL_OPEN_EVENT* item;
rdpChannels* channels;
struct channel_data* lchannel_data;
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
if ((channels == NULL) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
if ((!channels) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
{
DEBUG_CHANNELS("error bad channel handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
@ -803,13 +795,13 @@ static UINT32 FREERDP_CC MyVirtualChannelWrite(UINT32 openHandle, void* pData, U
return CHANNEL_RC_NOT_CONNECTED;
}
if (pData == 0)
if (!pData)
{
DEBUG_CHANNELS("error bad pData");
return CHANNEL_RC_NULL_DATA;
}
if (dataLength == 0)
if (!dataLength)
{
DEBUG_CHANNELS("error bad dataLength");
return CHANNEL_RC_ZERO_LENGTH;
@ -829,16 +821,13 @@ static UINT32 FREERDP_CC MyVirtualChannelWrite(UINT32 openHandle, void* pData, U
return CHANNEL_RC_NOT_CONNECTED;
}
item = (SYNC_DATA*) _aligned_malloc(sizeof(SYNC_DATA), MEMORY_ALLOCATION_ALIGNMENT);
item = (CHANNEL_OPEN_EVENT*) malloc(sizeof(CHANNEL_OPEN_EVENT));
item->Data = pData;
item->DataLength = dataLength;
item->UserData = pUserData;
item->Index = index;
InterlockedPushEntrySList(channels->pSyncDataList, &(item->ItemEntry));
/* set the event */
SetEvent(channels->signal);
MessageQueue_Post(channels->MsgPipe->Out, (void*) channels, 0, (void*) item, NULL);
return CHANNEL_RC_OK;
}
@ -851,7 +840,7 @@ static UINT32 FREERDP_CC MyVirtualChannelEventPush(UINT32 openHandle, RDP_EVENT*
channels = freerdp_channels_find_by_open_handle(openHandle, &index);
if ((channels == NULL) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
if ((!channels) || (index < 0) || (index >= CHANNEL_MAX_COUNT))
{
DEBUG_CHANNELS("error bad channels handle");
return CHANNEL_RC_BAD_CHANNEL_HANDLE;
@ -863,7 +852,7 @@ static UINT32 FREERDP_CC MyVirtualChannelEventPush(UINT32 openHandle, RDP_EVENT*
return CHANNEL_RC_NOT_CONNECTED;
}
if (event == NULL)
if (!event)
{
DEBUG_CHANNELS("error bad event");
return CHANNEL_RC_NULL_DATA;
@ -877,19 +866,20 @@ static UINT32 FREERDP_CC MyVirtualChannelEventPush(UINT32 openHandle, RDP_EVENT*
return CHANNEL_RC_NOT_OPEN;
}
/* lock channels->event */
WaitForSingleObject(channels->event_sem, INFINITE);
if (!channels->is_connected)
{
ReleaseSemaphore(channels->event_sem, 1, NULL);
DEBUG_CHANNELS("error not connected");
return CHANNEL_RC_NOT_CONNECTED;
}
channels->event = event;
/* set the event */
SetEvent(channels->signal);
/**
* We really intend to use the In queue for events, but we're pushing on both
* to wake up threads waiting on the out queue. Doing this cleanly would require
* breaking freerdp_pop_event() a bit too early in this refactoring.
*/
MessageQueue_Post(channels->MsgPipe->In, (void*) channels, 1, (void*) event, NULL);
MessageQueue_Post(channels->MsgPipe->Out, (void*) channels, 1, (void*) event, NULL);
return CHANNEL_RC_OK;
}
@ -929,11 +919,7 @@ rdpChannels* freerdp_channels_new(void)
channels = (rdpChannels*) malloc(sizeof(rdpChannels));
ZeroMemory(channels, sizeof(rdpChannels));
channels->pSyncDataList = (PSLIST_HEADER) _aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
InitializeSListHead(channels->pSyncDataList);
channels->event_sem = CreateSemaphore(NULL, 1, 16, NULL);
channels->signal = CreateEvent(NULL, TRUE, FALSE, NULL);
channels->MsgPipe = MessagePipe_New();
/* Add it to the global list */
channels_list = (rdpChannelsList*) malloc(sizeof(rdpChannelsList));
@ -953,11 +939,7 @@ void freerdp_channels_free(rdpChannels* channels)
rdpChannelsList* list;
rdpChannelsList* prev;
InterlockedFlushSList(channels->pSyncDataList);
_aligned_free(channels->pSyncDataList);
CloseHandle(channels->event_sem);
CloseHandle(channels->signal);
MessagePipe_Free(channels->MsgPipe);
/* Remove from global list */
@ -1144,15 +1126,15 @@ int freerdp_channels_data(freerdp* instance, int channel_id, void* data, int dat
channels = freerdp_channels_find_by_instance(instance);
if (channels == 0)
if (!channels)
{
DEBUG_CHANNELS("could not find channel manager");
return 1;
}
lrdp_channel = freerdp_channels_find_channel_by_id(channels, instance->settings,
channel_id, &index);
if (lrdp_channel == 0)
lrdp_channel = freerdp_channels_find_channel_by_id(channels, instance->settings, channel_id, &index);
if (!lrdp_channel)
{
DEBUG_CHANNELS("could not find channel id");
return 1;
@ -1160,7 +1142,7 @@ int freerdp_channels_data(freerdp* instance, int channel_id, void* data, int dat
lchannel_data = freerdp_channels_find_channel_data_by_name(channels, lrdp_channel->Name, &index);
if (lchannel_data == 0)
if (!lchannel_data)
{
DEBUG_CHANNELS("could not find channel name");
return 1;
@ -1198,7 +1180,7 @@ FREERDP_API int freerdp_channels_send_event(rdpChannels* channels, RDP_EVENT* ev
name = event_class_to_name_table[event->event_class];
if (name == NULL)
if (!name)
{
DEBUG_CHANNELS("unknown event_class %d", event->event_class);
freerdp_event_free(event);
@ -1207,17 +1189,16 @@ FREERDP_API int freerdp_channels_send_event(rdpChannels* channels, RDP_EVENT* ev
lchannel_data = freerdp_channels_find_channel_data_by_name(channels, name, &index);
if (lchannel_data == NULL)
if (!lchannel_data)
{
DEBUG_CHANNELS("could not find channel name %s", name);
freerdp_event_free(event);
return 1;
}
if (lchannel_data->open_event_proc != NULL)
if (lchannel_data->open_event_proc)
{
lchannel_data->open_event_proc(lchannel_data->open_handle,
CHANNEL_EVENT_USER,
lchannel_data->open_event_proc(lchannel_data->open_handle, CHANNEL_EVENT_USER,
event, sizeof(RDP_EVENT), sizeof(RDP_EVENT), 0);
}
@ -1229,13 +1210,20 @@ FREERDP_API int freerdp_channels_send_event(rdpChannels* channels, RDP_EVENT* ev
*/
static void freerdp_channels_process_sync(rdpChannels* channels, freerdp* instance)
{
SYNC_DATA* item;
wMessage message;
RDP_EVENT* event;
CHANNEL_OPEN_EVENT* item;
rdpChannel* lrdp_channel;
struct channel_data* lchannel_data;
while (QueryDepthSList(channels->pSyncDataList) > 0)
while (MessageQueue_Peek(channels->MsgPipe->Out, &message, TRUE))
{
item = (SYNC_DATA*) InterlockedPopEntrySList(channels->pSyncDataList);
if (message.id == WMQ_QUIT)
break;
if (message.id == 0)
{
item = (CHANNEL_OPEN_EVENT*) message.wParam;
if (!item)
break;
@ -1245,16 +1233,26 @@ static void freerdp_channels_process_sync(rdpChannels* channels, freerdp* instan
lrdp_channel = freerdp_channels_find_channel_by_name(channels, instance->settings,
lchannel_data->name, &item->Index);
if (lrdp_channel != NULL)
if (lrdp_channel)
instance->SendChannelData(instance, lrdp_channel->ChannelId, item->Data, item->DataLength);
if (lchannel_data->open_event_proc != 0)
if (lchannel_data->open_event_proc)
{
lchannel_data->open_event_proc(lchannel_data->open_handle,
CHANNEL_EVENT_WRITE_COMPLETE, item->UserData, sizeof(void*), sizeof(void*), 0);
CHANNEL_EVENT_WRITE_COMPLETE, item->UserData, item->DataLength, item->DataLength, 0);
}
_aligned_free(item);
free(item);
}
else if (message.id == 1)
{
event = (RDP_EVENT*) message.wParam;
/**
* Ignore for now, the same event is being pushed on the In queue,
* and we're pushing it on the Out queue just to wake other threads
*/
}
}
}
@ -1266,7 +1264,7 @@ BOOL freerdp_channels_get_fds(rdpChannels* channels, freerdp* instance, void** r
{
void* pfd;
pfd = GetEventWaitObject(channels->signal);
pfd = GetEventWaitObject(MessageQueue_Event(channels->MsgPipe->Out));
if (pfd)
{
@ -1283,7 +1281,7 @@ HANDLE freerdp_channels_get_event_handle(freerdp* instance)
rdpChannels* channels;
channels = instance->context->channels;
event = channels->signal;
event = MessageQueue_Event(channels->MsgPipe->Out);
return event;
}
@ -1294,9 +1292,8 @@ int freerdp_channels_process_pending_messages(freerdp* instance)
channels = instance->context->channels;
if (WaitForSingleObject(channels->signal, 0) == WAIT_OBJECT_0)
if (WaitForSingleObject(MessageQueue_Event(channels->MsgPipe->Out), 0) == WAIT_OBJECT_0)
{
ResetEvent(channels->signal);
freerdp_channels_process_sync(channels, instance);
}
@ -1308,9 +1305,8 @@ int freerdp_channels_process_pending_messages(freerdp* instance)
*/
BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance)
{
if (WaitForSingleObject(channels->signal, 0) == WAIT_OBJECT_0)
if (WaitForSingleObject(MessageQueue_Event(channels->MsgPipe->Out), 0) == WAIT_OBJECT_0)
{
ResetEvent(channels->signal);
freerdp_channels_process_sync(channels, instance);
}
@ -1319,16 +1315,16 @@ BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance)
RDP_EVENT* freerdp_channels_pop_event(rdpChannels* channels)
{
RDP_EVENT* event;
wMessage message;
RDP_EVENT* event = NULL;
if (channels->event == NULL)
return NULL;
event = channels->event;
channels->event = NULL;
/* release channels->event */
ReleaseSemaphore(channels->event_sem, 1, NULL);
if (MessageQueue_Peek(channels->MsgPipe->In, &message, TRUE))
{
if (message.id == 1)
{
event = (RDP_EVENT*) message.wParam;
}
}
return event;
}

View File

@ -470,14 +470,11 @@ static void drive_process_irp_query_volume_information(DRIVE_DEVICE* disk, IRP*
}
/* http://msdn.microsoft.com/en-us/library/cc241518.aspx */
static void drive_process_irp_silent_ignore(DRIVE_DEVICE* disk, IRP* irp)
{
UINT32 FsInformationClass;
UINT32 pad;
STREAM* output = irp->output;
char* volumeLabel;
int length;
int status;
stream_read_UINT32(irp->input, FsInformationClass);

View File

@ -268,7 +268,8 @@ static void* parallel_thread_func(void* arg)
while (1)
{
freerdp_thread_wait(parallel->thread);
if (freerdp_thread_wait(parallel->thread) < 0)
break;
if (freerdp_thread_is_stopped(parallel->thread))
break;

View File

@ -33,7 +33,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-utils)
MODULES winpr-sysinfo winpr-utils)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})

View File

@ -28,12 +28,14 @@
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
#include <alsa/asoundlib.h>
#include <freerdp/types.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/utils/svc_plugin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/debug.h>
#include "rdpsnd_main.h"
@ -43,94 +45,175 @@ struct rdpsnd_alsa_plugin
{
rdpsndDevicePlugin device;
int latency;
int wformat;
int block_size;
char* device_name;
snd_pcm_t* out_handle;
snd_pcm_t* pcm_handle;
snd_mixer_t* mixer_handle;
UINT32 source_rate;
UINT32 actual_rate;
UINT32 wLocalTimeClose;
snd_pcm_format_t format;
UINT32 source_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int block_size;
int latency;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
snd_pcm_uframes_t start_threshold;
snd_async_handler_t* pcm_callback;
FREERDP_DSP_CONTEXT* dsp_context;
};
static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
#define SND_PCM_CHECK(_func, _status) \
if (_status < 0) \
{ \
printf("%s: %d\n", _func, _status); \
return -1; \
}
int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
{
int status;
snd_pcm_hw_params_t* hw_params;
snd_pcm_sw_params_t* sw_params;
snd_pcm_uframes_t frames;
snd_pcm_uframes_t start_threshold;
snd_pcm_drop(alsa->out_handle);
snd_pcm_uframes_t buffer_size_max;
status = snd_pcm_hw_params_malloc(&hw_params);
SND_PCM_CHECK("snd_pcm_hw_params_malloc", status);
if (status < 0)
status = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params);
SND_PCM_CHECK("snd_pcm_hw_params_any", status);
/* Set interleaved read/write access */
status = snd_pcm_hw_params_set_access(alsa->pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
SND_PCM_CHECK("snd_pcm_hw_params_set_access", status);
/* Set sample format */
status = snd_pcm_hw_params_set_format(alsa->pcm_handle, hw_params, alsa->format);
SND_PCM_CHECK("snd_pcm_hw_params_set_format", status);
/* Set sample rate */
status = snd_pcm_hw_params_set_rate_near(alsa->pcm_handle, hw_params, &alsa->actual_rate, NULL);
SND_PCM_CHECK("snd_pcm_hw_params_set_rate_near", status);
/* Set number of channels */
status = snd_pcm_hw_params_set_channels(alsa->pcm_handle, hw_params, alsa->actual_channels);
SND_PCM_CHECK("snd_pcm_hw_params_set_channels", status);
/* Get maximum buffer size */
status = snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max);
SND_PCM_CHECK("snd_pcm_hw_params_get_buffer_size_max", status);
if (alsa->buffer_size > buffer_size_max)
{
DEBUG_WARN("snd_pcm_hw_params_malloc failed");
return;
printf("Warning: requested sound buffer size %d, got %d instead\n",
(int) alsa->buffer_size, (int) buffer_size_max);
alsa->buffer_size = buffer_size_max;
}
snd_pcm_hw_params_any(alsa->out_handle, hw_params);
snd_pcm_hw_params_set_access(alsa->out_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(alsa->out_handle, hw_params, alsa->format);
snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params, &alsa->actual_rate, NULL);
snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params, &alsa->actual_channels);
/* Set buffer size */
status = snd_pcm_hw_params_set_buffer_size_near(alsa->pcm_handle, hw_params, &alsa->buffer_size);
SND_PCM_CHECK("snd_pcm_hw_params_set_buffer_size_near", status);
if (alsa->latency < 0)
frames = alsa->actual_rate * 4 / 10; /* Default to 400ms buffer */
else
frames = alsa->latency * alsa->actual_rate * 2 / 1000; /* Double of the latency */
/* Get period size */
status = snd_pcm_hw_params_get_period_size_min(hw_params, &alsa->period_size, NULL);
SND_PCM_CHECK("snd_pcm_hw_params_get_period_size_min", status);
if (frames < alsa->actual_rate / 2)
frames = alsa->actual_rate / 2; /* Minimum 0.5-second buffer */
/* Set period size */
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size, NULL);
SND_PCM_CHECK("snd_pcm_hw_params_set_period_size_near", status);
status = snd_pcm_hw_params(alsa->pcm_handle, hw_params);
SND_PCM_CHECK("snd_pcm_hw_params", status);
snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params, &frames);
snd_pcm_hw_params(alsa->out_handle, hw_params);
snd_pcm_hw_params_free(hw_params);
status = snd_pcm_sw_params_malloc(&sw_params);
if (status < 0)
{
DEBUG_WARN("snd_pcm_sw_params_malloc failed");
return;
return 0;
}
snd_pcm_sw_params_current(alsa->out_handle, sw_params);
int rdpsnd_alsa_set_sw_params(rdpsndAlsaPlugin* alsa)
{
int status;
snd_pcm_sw_params_t* sw_params;
if (alsa->latency == 0)
start_threshold = 0;
else
start_threshold = frames / 2;
alsa->start_threshold = alsa->buffer_size;
status = snd_pcm_sw_params_malloc(&sw_params);
SND_PCM_CHECK("snd_pcm_sw_params_malloc", status);
status = snd_pcm_sw_params_current(alsa->pcm_handle, sw_params);
SND_PCM_CHECK("snd_pcm_sw_params_current", status);
status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->start_threshold);
SND_PCM_CHECK("snd_pcm_sw_params_set_start_threshold", status);
status = snd_pcm_sw_params(alsa->pcm_handle, sw_params);
SND_PCM_CHECK("snd_pcm_sw_params", status);
snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params, start_threshold);
snd_pcm_sw_params(alsa->out_handle, sw_params);
snd_pcm_sw_params_free(sw_params);
snd_pcm_prepare(alsa->out_handle);
status = snd_pcm_prepare(alsa->pcm_handle);
SND_PCM_CHECK("snd_pcm_prepare", status);
DEBUG_SVC("hardware buffer %d frames, playback buffer %.2g seconds",
(int) frames, (double) frames / 2.0 / (double) alsa->actual_rate);
return 0;
}
if ((alsa->actual_rate != alsa->source_rate) || (alsa->actual_channels != alsa->source_channels))
int rdpsnd_alsa_validate_params(rdpsndAlsaPlugin* alsa)
{
DEBUG_SVC("actual rate %d / channel %d is different from source rate %d / channel %d, resampling required.",
alsa->actual_rate, alsa->actual_channels, alsa->source_rate, alsa->source_channels);
}
int status;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
status = snd_pcm_get_params(alsa->pcm_handle, &buffer_size, &period_size);
SND_PCM_CHECK("snd_pcm_get_params", status);
return 0;
}
static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static int rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
{
/**
* ALSA Parameters
*
* http://www.alsa-project.org/main/index.php/FramesPeriods
*
* buffer_size = period_size * periods
* period_bytes = period_size * bytes_per_frame
* bytes_per_frame = channels * bytes_per_sample
*
* A frame is equivalent of one sample being played,
* irrespective of the number of channels or the number of bits
*
* A period is the number of frames in between each hardware interrupt.
*
* The buffer size always has to be greater than one period size.
* Commonly this is (2 * period_size), but some hardware can do 8 periods per buffer.
* It is also possible for the buffer size to not be an integer multiple of the period size.
*/
snd_pcm_drop(alsa->pcm_handle);
if (alsa->latency < 0)
alsa->latency = 400;
alsa->buffer_size = alsa->latency * (alsa->actual_rate / 1000);
if (rdpsnd_alsa_set_hw_params(alsa) < 0)
return -1;
if (rdpsnd_alsa_set_sw_params(alsa) < 0)
return -1;
rdpsnd_alsa_validate_params(alsa);
return 0;
}
static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (format != NULL)
if (format)
{
alsa->source_rate = format->nSamplesPerSec;
alsa->actual_rate = format->nSamplesPerSec;
@ -139,13 +222,19 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 4:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
break;
case 8:
alsa->format = SND_PCM_FORMAT_S8;
alsa->bytes_per_channel = 1;
break;
case 16:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
@ -153,8 +242,8 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for
}
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
break;
@ -172,9 +261,11 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for
static void rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
{
int status;
snd_mixer_t* handle;
status = snd_mixer_open(&handle, 0);
if (alsa->mixer_handle)
return;
status = snd_mixer_open(&alsa->mixer_handle, 0);
if (status < 0)
{
@ -182,47 +273,47 @@ static void rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
return;
}
status = snd_mixer_attach(handle, alsa->device_name);
status = snd_mixer_attach(alsa->mixer_handle, alsa->device_name);
if (status < 0)
{
DEBUG_WARN("snd_mixer_attach failed");
snd_mixer_close(handle);
snd_mixer_close(alsa->mixer_handle);
return;
}
status = snd_mixer_selem_register(handle, NULL, NULL);
status = snd_mixer_selem_register(alsa->mixer_handle, NULL, NULL);
if (status < 0)
{
DEBUG_WARN("snd_mixer_selem_register failed");
snd_mixer_close(handle);
snd_mixer_close(alsa->mixer_handle);
return;
}
status = snd_mixer_load(handle);
status = snd_mixer_load(alsa->mixer_handle);
if (status < 0)
{
DEBUG_WARN("snd_mixer_load failed");
snd_mixer_close(handle);
snd_mixer_close(alsa->mixer_handle);
return;
}
alsa->mixer_handle = handle;
}
static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
int mode;
int status;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->out_handle != 0)
if (alsa->pcm_handle)
return;
DEBUG_SVC("opening");
mode = 0;
//mode |= SND_PCM_NONBLOCK;
status = snd_pcm_open(&alsa->out_handle, alsa->device_name, SND_PCM_STREAM_PLAYBACK, 0);
status = snd_pcm_open(&alsa->pcm_handle, alsa->device_name, SND_PCM_STREAM_PLAYBACK, mode);
if (status < 0)
{
@ -238,14 +329,32 @@ static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format, i
static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
{
int status;
snd_htimestamp_t tstamp;
snd_pcm_uframes_t frames;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->out_handle != 0)
if (!alsa->pcm_handle)
return;
status = snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp);
if (status != 0)
frames = 0;
alsa->wLocalTimeClose = GetTickCount();
alsa->wLocalTimeClose += (((frames * 1000) / alsa->actual_rate) / alsa->actual_channels);
}
static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
{
DEBUG_SVC("close");
snd_pcm_drain(alsa->out_handle);
snd_pcm_close(alsa->out_handle);
alsa->out_handle = 0;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->pcm_handle)
{
snd_pcm_drain(alsa->pcm_handle);
snd_pcm_close(alsa->pcm_handle);
alsa->pcm_handle = 0;
}
if (alsa->mixer_handle)
@ -253,23 +362,19 @@ static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
snd_mixer_close(alsa->mixer_handle);
alsa->mixer_handle = NULL;
}
}
static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device;
rdpsnd_alsa_close(device);
free(alsa->device_name);
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa);
}
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, rdpsndFormat* format)
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
format->nSamplesPerSec <= 48000 &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
@ -279,8 +384,8 @@ static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, rdpsndForma
}
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if (format->nSamplesPerSec <= 48000 &&
format->wBitsPerSample == 4 &&
(format->nChannels == 1 || format->nChannels == 2))
@ -288,10 +393,63 @@ static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, rdpsndForma
return TRUE;
}
break;
case WAVE_FORMAT_ALAW:
break;
case WAVE_FORMAT_MULAW:
break;
case WAVE_FORMAT_GSM610:
break;
}
return FALSE;
}
static UINT32 rdpsnd_alsa_get_volume(rdpsndDevicePlugin* device)
{
long volume_min;
long volume_max;
long volume_left;
long volume_right;
int percent_left;
int percent_right;
UINT32 dwVolume;
UINT16 dwVolumeLeft;
UINT16 dwVolumeRight;
snd_mixer_elem_t* elem;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
if (!alsa->mixer_handle)
rdpsnd_alsa_open_mixer(alsa);
for (elem = snd_mixer_first_elem(alsa->mixer_handle); elem; elem = snd_mixer_elem_next(elem))
{
if (snd_mixer_selem_has_playback_volume(elem))
{
snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max);
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &volume_left);
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &volume_right);
dwVolumeLeft = (UINT16) (((volume_left * 0xFFFF) - volume_min) / (volume_max - volume_min));
dwVolumeRight = (UINT16) (((volume_right * 0xFFFF) - volume_min) / (volume_max - volume_min));
percent_left = (dwVolumeLeft * 100) / 0xFFFF;
percent_right = (dwVolumeRight * 100) / 0xFFFF;
break;
}
}
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
return dwVolume;
}
static void rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{
long left;
@ -300,15 +458,20 @@ static void rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
long volume_max;
long volume_left;
long volume_right;
int percent_left;
int percent_right;
snd_mixer_elem_t* elem;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (!alsa->mixer_handle)
return;
rdpsnd_alsa_open_mixer(alsa);
left = (value & 0xFFFF);
right = ((value >> 16) & 0xFFFF);
percent_left = (left * 100) / 0xFFFF;
percent_right = (right * 100) / 0xFFFF;
for (elem = snd_mixer_first_elem(alsa->mixer_handle); elem; elem = snd_mixer_elem_next(elem))
{
if (snd_mixer_selem_has_playback_volume(elem))
@ -322,99 +485,141 @@ static void rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
}
}
static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, BYTE* data, int size)
BYTE* rdpsnd_alsa_process_audio_sample(rdpsndDevicePlugin* device, BYTE* data, int* size)
{
BYTE* src;
int len;
int status;
int frames;
int rbytes_per_frame;
int sbytes_per_frame;
BYTE* pindex;
BYTE* end;
BYTE* srcData;
int srcFrameSize;
int dstFrameSize;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->out_handle == 0)
return;
if (alsa->wformat == 2)
if (alsa->wformat == WAVE_FORMAT_ADPCM)
{
alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context,
data, size, alsa->source_channels, alsa->block_size);
size = alsa->dsp_context->adpcm_size;
src = alsa->dsp_context->adpcm_buffer;
data, *size, alsa->source_channels, alsa->block_size);
*size = alsa->dsp_context->adpcm_size;
srcData = alsa->dsp_context->adpcm_buffer;
}
else if (alsa->wformat == 0x11)
else if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
{
alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context,
data, size, alsa->source_channels, alsa->block_size);
size = alsa->dsp_context->adpcm_size;
src = alsa->dsp_context->adpcm_buffer;
data, *size, alsa->source_channels, alsa->block_size);
*size = alsa->dsp_context->adpcm_size;
srcData = alsa->dsp_context->adpcm_buffer;
}
else
{
src = data;
srcData = data;
}
sbytes_per_frame = alsa->source_channels * alsa->bytes_per_channel;
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
srcFrameSize = alsa->source_channels * alsa->bytes_per_channel;
dstFrameSize = alsa->actual_channels * alsa->bytes_per_channel;
if ((size % sbytes_per_frame) != 0)
{
DEBUG_WARN("error len mod");
return;
}
if ((*size % srcFrameSize) != 0)
return NULL;
if ((alsa->source_rate == alsa->actual_rate) && (alsa->source_channels == alsa->actual_channels))
if (!((alsa->source_rate == alsa->actual_rate) && (alsa->source_channels == alsa->actual_channels)))
{
}
else
{
alsa->dsp_context->resample(alsa->dsp_context, src, alsa->bytes_per_channel,
alsa->source_channels, alsa->source_rate, size / sbytes_per_frame,
alsa->dsp_context->resample(alsa->dsp_context, srcData, alsa->bytes_per_channel,
alsa->source_channels, alsa->source_rate, *size / srcFrameSize,
alsa->actual_channels, alsa->actual_rate);
frames = alsa->dsp_context->resampled_frames;
DEBUG_SVC("resampled %d frames at %d to %d frames at %d",
size / sbytes_per_frame, alsa->source_rate, frames, alsa->actual_rate);
size = frames * rbytes_per_frame;
src = alsa->dsp_context->resampled_buffer;
*size = frames * dstFrameSize;
srcData = alsa->dsp_context->resampled_buffer;
}
pindex = src;
end = pindex + size;
data = srcData;
while (pindex < end)
return data;
}
static void rdpsnd_alsa_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
len = end - pindex;
frames = len / rbytes_per_frame;
status = snd_pcm_writei(alsa->out_handle, pindex, frames);
int size;
BYTE* data;
size = wave->length;
data = rdpsnd_alsa_process_audio_sample(device, wave->data, &size);
wave->data = (BYTE*) malloc(size);
CopyMemory(wave->data, data, size);
wave->length = size;
}
static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
BYTE* data;
int offset;
int length;
int status;
int frame_size;
UINT32 wCurrentTime;
snd_htimestamp_t tstamp;
snd_pcm_uframes_t frames;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
offset = 0;
data = wave->data;
length = wave->length;
frame_size = alsa->actual_channels * alsa->bytes_per_channel;
if (alsa->wLocalTimeClose)
{
wCurrentTime = GetTickCount();
if (snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp) == -EPIPE)
{
if (wCurrentTime > alsa->wLocalTimeClose)
snd_pcm_recover(alsa->pcm_handle, -EPIPE, 1);
}
alsa->wLocalTimeClose = 0;
}
while (offset < length)
{
status = snd_pcm_writei(alsa->pcm_handle, &data[offset], (length - offset) / frame_size);
if (status == -EPIPE)
{
snd_pcm_recover(alsa->out_handle, status, 0);
snd_pcm_recover(alsa->pcm_handle, status, 0);
status = 0;
}
else if (status == -EAGAIN)
{
status = 0;
}
else if (status < 0)
{
DEBUG_WARN("status %d", status);
snd_pcm_close(alsa->out_handle);
alsa->out_handle = 0;
rdpsnd_alsa_open(device, NULL, alsa->latency);
printf("status: %d\n", status);
snd_pcm_close(alsa->pcm_handle);
alsa->pcm_handle = NULL;
rdpsnd_alsa_open((rdpsndDevicePlugin*) alsa, NULL, alsa->latency);
break;
}
pindex += status * rbytes_per_frame;
}
offset += status * frame_size;
}
static void rdpsnd_alsa_start(rdpsndDevicePlugin* device)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
free(data);
if (alsa->out_handle == 0)
return;
snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp);
snd_pcm_start(alsa->out_handle);
wave->wPlaybackDelay = ((frames * 1000) / alsa->actual_rate);
wave->wLocalTimeB = GetTickCount();
wave->wLocalTimeB += wave->wPlaybackDelay;
wave->wLatency = (UINT16) (wave->wLocalTimeB - wave->wLocalTimeA);
wave->wTimeStampB = wave->wTimeStampA + wave->wLatency;
//printf("wTimeStampA: %d wTimeStampB: %d wLatency: %d\n", wave->wTimeStampA, wave->wTimeStampB, wave->wLatency);
device->WaveConfirm(device, wave);
}
COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] =
@ -468,9 +673,10 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
alsa->device.Open = rdpsnd_alsa_open;
alsa->device.FormatSupported = rdpsnd_alsa_format_supported;
alsa->device.SetFormat = rdpsnd_alsa_set_format;
alsa->device.GetVolume = rdpsnd_alsa_get_volume;
alsa->device.SetVolume = rdpsnd_alsa_set_volume;
alsa->device.Play = rdpsnd_alsa_play;
alsa->device.Start = rdpsnd_alsa_start;
alsa->device.WaveDecode = rdpsnd_alsa_wave_decode;
alsa->device.WavePlay = rdpsnd_alsa_wave_play;
alsa->device.Close = rdpsnd_alsa_close;
alsa->device.Free = rdpsnd_alsa_free;
@ -480,7 +686,7 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
if (!alsa->device_name)
alsa->device_name = _strdup("default");
alsa->out_handle = 0;
alsa->pcm_handle = 0;
alsa->source_rate = 22050;
alsa->actual_rate = 22050;
alsa->format = SND_PCM_FORMAT_S16_LE;

View File

@ -32,7 +32,7 @@
#include <winpr/crt.h>
#include <freerdp/types.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/svc_plugin.h>
#include <AudioToolbox/AudioToolbox.h>
@ -72,7 +72,7 @@ static void rdpsnd_audio_close(rdpsndDevicePlugin* device)
aq_plugin_p->is_open = 0;
}
static void rdpsnd_audio_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_audio_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
int rv;
int i;
@ -127,7 +127,7 @@ static void rdpsnd_audio_free(rdpsndDevicePlugin* device)
{
}
static BOOL rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, rdpsndFormat* format)
static BOOL rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
@ -144,7 +144,7 @@ static BOOL rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, rdpsndForm
return 0;
}
static void rdpsnd_audio_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_audio_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
}

View File

@ -31,7 +31,7 @@
#include <pulse/pulseaudio.h>
#include <freerdp/types.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/svc_plugin.h>
#include "rdpsnd_main.h"
@ -106,13 +106,16 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
for (;;)
{
state = pa_context_get_state(pulse->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state))
{
DEBUG_WARN("bad context state (%d)", pa_context_errno(pulse->context));
break;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
@ -139,7 +142,7 @@ static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success,
static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation)
{
if (operation == NULL)
if (!operation)
return;
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
@ -200,7 +203,7 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
pa_threaded_mainloop_unlock(pulse->mainloop);
}
static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat* format)
static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT* format)
{
pa_sample_spec sample_spec = { 0 };
@ -212,7 +215,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat*
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
@ -224,17 +227,20 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat*
}
break;
case 6: /* A-LAW */
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
sample_spec.format = PA_SAMPLE_S16LE;
break;
case WAVE_FORMAT_ALAW:
sample_spec.format = PA_SAMPLE_ALAW;
break;
case 7: /* U-LAW */
case WAVE_FORMAT_MULAW:
sample_spec.format = PA_SAMPLE_ULAW;
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
sample_spec.format = PA_SAMPLE_S16LE;
case WAVE_FORMAT_GSM610:
break;
}
@ -243,7 +249,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat*
pulse->block_size = format->nBlockAlign;
}
static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
pa_stream_state_t state;
pa_stream_flags_t flags;
@ -278,7 +284,7 @@ static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, rdpsndFormat* format,
return;
}
/* install essential callbacks */
/* register essential callbacks */
pa_stream_set_state_callback(pulse->stream, rdpsnd_pulse_stream_state_callback, pulse);
pa_stream_set_write_callback(pulse->stream, rdpsnd_pulse_stream_request_callback, pulse);
@ -365,7 +371,7 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
free(pulse);
}
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndFormat* format)
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
@ -374,7 +380,7 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndForm
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
@ -384,8 +390,8 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndForm
}
break;
case 6: /* A-LAW */
case 7: /* U-LAW */
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
@ -395,8 +401,8 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndForm
}
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
@ -408,7 +414,7 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndForm
return FALSE;
}
static void rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
@ -463,17 +469,19 @@ static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
if (!pulse->stream)
return;
if (pulse->format == 2)
if (pulse->format == WAVE_FORMAT_ADPCM)
{
pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context,
data, size, pulse->sample_spec.channels, pulse->block_size);
size = pulse->dsp_context->adpcm_size;
src = pulse->dsp_context->adpcm_buffer;
}
else if (pulse->format == 0x11)
else if (pulse->format == WAVE_FORMAT_DVI_ADPCM)
{
pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context,
data, size, pulse->sample_spec.channels, pulse->block_size);
size = pulse->dsp_context->adpcm_size;
src = pulse->dsp_context->adpcm_buffer;
}

View File

@ -24,6 +24,7 @@
#ifndef _WIN32
#include <sys/time.h>
#include <signal.h>
#endif
#include <stdio.h>
@ -31,41 +32,49 @@
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/print.h>
#include <winpr/cmdline.h>
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/constants.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/list.h>
#include <freerdp/utils/signal.h>
#include <freerdp/utils/svc_plugin.h>
#include "rdpsnd_main.h"
#define TIME_DELAY_MS 65
struct rdpsnd_plugin
{
rdpSvcPlugin plugin;
LIST* data_out_list;
HANDLE thread;
wMessageQueue* queue;
BYTE cBlockNo;
rdpsndFormat* supported_formats;
int n_supported_formats;
int current_format;
int wCurrentFormatNo;
AUDIO_FORMAT* ServerFormats;
UINT16 NumberOfServerFormats;
AUDIO_FORMAT* ClientFormats;
UINT16 NumberOfClientFormats;
BOOL expectingWave;
BYTE waveData[4];
UINT16 waveDataSize;
UINT32 wTimeStamp; /* server timestamp */
UINT32 wave_timestamp; /* client timestamp */
UINT32 wTimeStamp;
BOOL is_open;
UINT32 close_timestamp;
UINT16 fixed_format;
UINT16 fixed_channel;
UINT32 fixed_rate;
int latency;
BOOL isOpen;
UINT16 fixedFormat;
UINT16 fixedChannel;
UINT32 fixedRate;
char* subsystem;
char* device_name;
@ -74,339 +83,484 @@ struct rdpsnd_plugin
rdpsndDevicePlugin* device;
};
struct data_out_item
void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo);
static void* rdpsnd_schedule_thread(void* arg)
{
STREAM* data_out;
UINT32 out_timestamp;
};
wMessage message;
UINT16 wTimeDiff;
UINT16 wTimeStamp;
UINT16 wCurrentTime;
RDPSND_WAVE* wave;
rdpsndPlugin* rdpsnd = (rdpsndPlugin*) arg;
/* get time in milliseconds */
static UINT32 get_mstime(void)
while (1)
{
#ifndef _WIN32
struct timeval tp;
gettimeofday(&tp, 0);
return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
#else
FILETIME ft;
UINT64 time64 = 0;
if (!MessageQueue_Wait(rdpsnd->queue))
break;
GetSystemTimeAsFileTime(&ft);
if (!MessageQueue_Peek(rdpsnd->queue, &message, TRUE))
break;
time64 |= ft.dwHighDateTime;
time64 <<= 32;
time64 |= ft.dwLowDateTime;
time64 /= 10000;
if (message.id == WMQ_QUIT)
break;
/* fix epoch? */
wave = (RDPSND_WAVE*) message.wParam;
wCurrentTime = (UINT16) GetTickCount();
wTimeStamp = wave->wLocalTimeB;
return (UINT32) time64;
if (wCurrentTime <= wTimeStamp)
{
wTimeDiff = wTimeStamp - wCurrentTime;
Sleep(wTimeDiff);
}
rdpsnd_send_wave_confirm_pdu(rdpsnd, wave->wTimeStampB, wave->cBlockNo);
free(wave);
}
return NULL;
}
void rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd)
{
STREAM* pdu;
pdu = stream_new(8);
stream_write_BYTE(pdu, SNDC_QUALITYMODE); /* msgType */
stream_write_BYTE(pdu, 0); /* bPad */
stream_write_UINT16(pdu, 4); /* BodySize */
stream_write_UINT16(pdu, HIGH_QUALITY); /* wQualityMode */
stream_write_UINT16(pdu, 0); /* Reserved */
svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
}
void rdpsnd_free_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
{
int index;
AUDIO_FORMAT* format;
if (formats)
{
for (index = 0; index < (int) count; index++)
{
format = &formats[index];
if (format->cbSize)
free(format->data);
}
free(formats);
}
}
char* rdpsnd_get_audio_tag_string(UINT16 wFormatTag)
{
switch (wFormatTag)
{
case WAVE_FORMAT_PCM:
return "WAVE_FORMAT_PCM";
case WAVE_FORMAT_ADPCM:
return "WAVE_FORMAT_ADPCM";
case WAVE_FORMAT_ALAW:
return "WAVE_FORMAT_ALAW";
case WAVE_FORMAT_MULAW:
return "WAVE_FORMAT_MULAW";
case WAVE_FORMAT_DVI_ADPCM:
return "WAVE_FORMAT_DVI_ADPCM";
case WAVE_FORMAT_GSM610:
return "WAVE_FORMAT_GSM610";
case WAVE_FORMAT_MSG723:
return "WAVE_FORMAT_MSG723";
case WAVE_FORMAT_DSPGROUP_TRUESPEECH:
return "WAVE_FORMAT_DSPGROUP_TRUESPEECH ";
case WAVE_FORMAT_MPEGLAYER3:
return "WAVE_FORMAT_MPEGLAYER3";
case WAVE_FORMAT_WMAUDIO2:
return "WAVE_FORMAT_WMAUDIO2";
}
return "WAVE_FORMAT_UNKNOWN";
}
void rdpsnd_print_audio_format(AUDIO_FORMAT* format)
{
printf("%s:\t wFormatTag: 0x%04X nChannels: %d nSamplesPerSec: %d nAvgBytesPerSec: %d nBlockAlign: %d wBitsPerSample: %d cbSize: %d\n",
rdpsnd_get_audio_tag_string(format->wFormatTag), format->wFormatTag,
format->nChannels, format->nSamplesPerSec, format->nAvgBytesPerSec,
format->nBlockAlign, format->wBitsPerSample, format->cbSize);
}
void rdpsnd_print_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
{
int index;
AUDIO_FORMAT* format;
if (formats)
{
printf("AUDIO_FORMATS (%d) =\n{\n", count);
for (index = 0; index < (int) count; index++)
{
format = &formats[index];
printf("\t");
rdpsnd_print_audio_format(format);
}
printf("}\n");
}
}
UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
{
UINT32 mstime;
UINT32 wSamples;
/**
* [MSDN-AUDIOFORMAT]:
* http://msdn.microsoft.com/en-us/library/ms713497.aspx
*/
wSamples = (size * 8) / format->wBitsPerSample;
mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels);
return mstime;
}
void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd)
{
int index;
AUDIO_FORMAT* serverFormat;
AUDIO_FORMAT* clientFormat;
rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
rdpsnd->NumberOfClientFormats = 0;
rdpsnd->ClientFormats = NULL;
rdpsnd->ClientFormats = (AUDIO_FORMAT*) malloc(sizeof(AUDIO_FORMAT) * rdpsnd->NumberOfServerFormats);
for (index = 0; index < (int) rdpsnd->NumberOfServerFormats; index++)
{
serverFormat = &rdpsnd->ServerFormats[index];
if (rdpsnd->fixedFormat > 0 && (rdpsnd->fixedFormat != serverFormat->wFormatTag))
continue;
if (rdpsnd->fixedChannel > 0 && (rdpsnd->fixedChannel != serverFormat->nChannels))
continue;
if (rdpsnd->fixedRate > 0 && (rdpsnd->fixedRate != serverFormat->nSamplesPerSec))
continue;
if (rdpsnd->device && rdpsnd->device->FormatSupported(rdpsnd->device, serverFormat))
{
clientFormat = &rdpsnd->ClientFormats[rdpsnd->NumberOfClientFormats++];
CopyMemory(clientFormat, serverFormat, sizeof(AUDIO_FORMAT));
clientFormat->cbSize = 0;
if (serverFormat->cbSize > 0)
{
clientFormat->data = (BYTE*) malloc(serverFormat->cbSize);
CopyMemory(clientFormat->data, serverFormat->data, serverFormat->cbSize);
clientFormat->cbSize = serverFormat->cbSize;
}
}
}
#if 0
printf("Server ");
rdpsnd_print_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
printf("\n");
printf("Client ");
rdpsnd_print_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
printf("\n");
#endif
}
/* process the linked list of data that has queued to be sent */
static void rdpsnd_process_interval(rdpSvcPlugin* plugin)
{
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)plugin;
struct data_out_item* item;
UINT32 cur_time;
while (list_size(rdpsnd->data_out_list) > 0)
{
item = (struct data_out_item*) list_peek(rdpsnd->data_out_list);
cur_time = get_mstime();
if (!item || cur_time <= item->out_timestamp)
break;
item = (struct data_out_item*) list_dequeue(rdpsnd->data_out_list);
svc_plugin_send(plugin, item->data_out);
free(item);
DEBUG_SVC("processed data_out");
}
if (rdpsnd->is_open && rdpsnd->close_timestamp > 0)
{
cur_time = get_mstime();
if (cur_time > rdpsnd->close_timestamp)
{
if (rdpsnd->device)
IFCALL(rdpsnd->device->Close, rdpsnd->device);
rdpsnd->is_open = FALSE;
rdpsnd->close_timestamp = 0;
DEBUG_SVC("processed close");
}
}
if (list_size(rdpsnd->data_out_list) == 0 && !rdpsnd->is_open)
{
rdpsnd->plugin.interval_ms = 0;
}
}
static void rdpsnd_free_supported_formats(rdpsndPlugin* rdpsnd)
{
UINT16 i;
for (i = 0; i < rdpsnd->n_supported_formats; i++)
free(rdpsnd->supported_formats[i].data);
free(rdpsnd->supported_formats);
rdpsnd->supported_formats = NULL;
rdpsnd->n_supported_formats = 0;
}
/* receives a list of server supported formats and returns a list
of client supported formats */
static void rdpsnd_process_message_formats(rdpsndPlugin* rdpsnd, STREAM* data_in)
void rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
{
int index;
STREAM* pdu;
UINT16 length;
UINT32 dwVolume;
UINT16 dwVolumeLeft;
UINT16 dwVolumeRight;
UINT16 wNumberOfFormats;
UINT16 nFormat;
UINT16 wVersion;
STREAM* data_out;
rdpsndFormat* out_formats;
UINT16 n_out_formats;
rdpsndFormat* format;
BYTE* format_mark;
BYTE* data_mark;
int pos;
AUDIO_FORMAT* clientFormat;
rdpsnd_free_supported_formats(rdpsnd);
stream_seek_UINT32(data_in); /* dwFlags */
stream_seek_UINT32(data_in); /* dwVolume */
stream_seek_UINT32(data_in); /* dwPitch */
stream_seek_UINT16(data_in); /* wDGramPort */
stream_read_UINT16(data_in, wNumberOfFormats);
stream_read_BYTE(data_in, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
stream_read_UINT16(data_in, wVersion);
stream_seek_BYTE(data_in); /* bPad */
DEBUG_SVC("wNumberOfFormats %d wVersion %d", wNumberOfFormats, wVersion);
if (wNumberOfFormats < 1)
if (rdpsnd->device->GetVolume)
{
DEBUG_WARN("wNumberOfFormats is 0");
return;
}
out_formats = (rdpsndFormat*) malloc(wNumberOfFormats * sizeof(rdpsndFormat));
ZeroMemory(out_formats, wNumberOfFormats * sizeof(rdpsndFormat));
n_out_formats = 0;
data_out = stream_new(24);
stream_write_BYTE(data_out, SNDC_FORMATS); /* msgType */
stream_write_BYTE(data_out, 0); /* bPad */
stream_seek_UINT16(data_out); /* BodySize */
stream_write_UINT32(data_out, TSSNDCAPS_ALIVE | TSSNDCAPS_VOLUME); /* dwFlags */
stream_write_UINT32(data_out, 0xFFFFFFFF); /* dwVolume */
stream_write_UINT32(data_out, 0); /* dwPitch */
stream_write_UINT16_be(data_out, 0); /* wDGramPort */
stream_seek_UINT16(data_out); /* wNumberOfFormats */
stream_write_BYTE(data_out, 0); /* cLastBlockConfirmed */
stream_write_UINT16(data_out, 6); /* wVersion */
stream_write_BYTE(data_out, 0); /* bPad */
for (nFormat = 0; nFormat < wNumberOfFormats; nFormat++)
{
stream_get_mark(data_in, format_mark);
format = &out_formats[n_out_formats];
stream_read_UINT16(data_in, format->wFormatTag);
stream_read_UINT16(data_in, format->nChannels);
stream_read_UINT32(data_in, format->nSamplesPerSec);
stream_seek_UINT32(data_in); /* nAvgBytesPerSec */
stream_read_UINT16(data_in, format->nBlockAlign);
stream_read_UINT16(data_in, format->wBitsPerSample);
stream_read_UINT16(data_in, format->cbSize);
stream_get_mark(data_in, data_mark);
stream_seek(data_in, format->cbSize);
format->data = NULL;
DEBUG_SVC("wFormatTag=%d nChannels=%d nSamplesPerSec=%d nBlockAlign=%d wBitsPerSample=%d",
format->wFormatTag, format->nChannels, format->nSamplesPerSec,
format->nBlockAlign, format->wBitsPerSample);
if (rdpsnd->fixed_format > 0 && rdpsnd->fixed_format != format->wFormatTag)
continue;
if (rdpsnd->fixed_channel > 0 && rdpsnd->fixed_channel != format->nChannels)
continue;
if (rdpsnd->fixed_rate > 0 && rdpsnd->fixed_rate != format->nSamplesPerSec)
continue;
if (rdpsnd->device && rdpsnd->device->FormatSupported(rdpsnd->device, format))
{
DEBUG_SVC("format supported.");
stream_check_size(data_out, 18 + format->cbSize);
stream_write(data_out, format_mark, 18 + format->cbSize);
if (format->cbSize > 0)
{
format->data = malloc(format->cbSize);
memcpy(format->data, data_mark, format->cbSize);
}
n_out_formats++;
}
}
rdpsnd->n_supported_formats = n_out_formats;
if (n_out_formats > 0)
{
rdpsnd->supported_formats = out_formats;
dwVolume = rdpsnd->device->GetVolume(rdpsnd->device);
}
else
{
free(out_formats);
DEBUG_WARN("no formats supported");
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
}
pos = stream_get_pos(data_out);
stream_set_pos(data_out, 2);
stream_write_UINT16(data_out, pos - 4);
stream_set_pos(data_out, 18);
stream_write_UINT16(data_out, n_out_formats);
stream_set_pos(data_out, pos);
wNumberOfFormats = rdpsnd->NumberOfClientFormats;
svc_plugin_send((rdpSvcPlugin*)rdpsnd, data_out);
length = 4 + 20;
for (index = 0; index < (int) wNumberOfFormats; index++)
length += (18 + rdpsnd->ClientFormats[index].cbSize);
pdu = stream_new(length);
stream_write_BYTE(pdu, SNDC_FORMATS); /* msgType */
stream_write_BYTE(pdu, 0); /* bPad */
stream_write_UINT16(pdu, length - 4); /* BodySize */
stream_write_UINT32(pdu, TSSNDCAPS_ALIVE | TSSNDCAPS_VOLUME); /* dwFlags */
stream_write_UINT32(pdu, dwVolume); /* dwVolume */
stream_write_UINT32(pdu, 0); /* dwPitch */
stream_write_UINT16(pdu, 0); /* wDGramPort */
stream_write_UINT16(pdu, wNumberOfFormats); /* wNumberOfFormats */
stream_write_BYTE(pdu, 0); /* cLastBlockConfirmed */
stream_write_UINT16(pdu, 6); /* wVersion */
stream_write_BYTE(pdu, 0); /* bPad */
for (index = 0; index < (int) wNumberOfFormats; index++)
{
clientFormat = &rdpsnd->ClientFormats[index];
stream_write_UINT16(pdu, clientFormat->wFormatTag);
stream_write_UINT16(pdu, clientFormat->nChannels);
stream_write_UINT32(pdu, clientFormat->nSamplesPerSec);
stream_write_UINT32(pdu, clientFormat->nAvgBytesPerSec);
stream_write_UINT16(pdu, clientFormat->nBlockAlign);
stream_write_UINT16(pdu, clientFormat->wBitsPerSample);
stream_write_UINT16(pdu, clientFormat->cbSize);
if (clientFormat->cbSize > 0)
stream_write(pdu, clientFormat->data, clientFormat->cbSize);
}
svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
}
void rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
{
int index;
UINT16 wVersion;
AUDIO_FORMAT* format;
UINT16 wNumberOfFormats;
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_seek_UINT32(s); /* dwPitch */
stream_seek_UINT16(s); /* wDGramPort */
stream_read_UINT16(s, wNumberOfFormats);
stream_read_BYTE(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
stream_read_UINT16(s, wVersion); /* wVersion */
stream_seek_BYTE(s); /* bPad */
rdpsnd->NumberOfServerFormats = wNumberOfFormats;
rdpsnd->ServerFormats = (AUDIO_FORMAT*) malloc(sizeof(AUDIO_FORMAT) * wNumberOfFormats);
for (index = 0; index < (int) wNumberOfFormats; index++)
{
format = &rdpsnd->ServerFormats[index];
stream_read_UINT16(s, format->wFormatTag); /* wFormatTag */
stream_read_UINT16(s, format->nChannels); /* nChannels */
stream_read_UINT32(s, format->nSamplesPerSec); /* nSamplesPerSec */
stream_read_UINT32(s, format->nAvgBytesPerSec); /* nAvgBytesPerSec */
stream_read_UINT16(s, format->nBlockAlign); /* nBlockAlign */
stream_read_UINT16(s, format->wBitsPerSample); /* wBitsPerSample */
stream_read_UINT16(s, format->cbSize); /* cbSize */
format->data = (BYTE*) malloc(format->cbSize);
stream_read(s, format->data, format->cbSize);
}
rdpsnd_select_supported_audio_formats(rdpsnd);
rdpsnd_send_client_audio_formats(rdpsnd);
if (wVersion >= 6)
rdpsnd_send_quality_mode_pdu(rdpsnd);
}
void rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, UINT16 wPackSize)
{
data_out = stream_new(8);
stream_write_BYTE(data_out, SNDC_QUALITYMODE); /* msgType */
stream_write_BYTE(data_out, 0); /* bPad */
stream_write_UINT16(data_out, 4); /* BodySize */
stream_write_UINT16(data_out, HIGH_QUALITY); /* wQualityMode */
stream_write_UINT16(data_out, 0); /* Reserved */
STREAM* pdu;
svc_plugin_send((rdpSvcPlugin*)rdpsnd, data_out);
}
pdu = stream_new(8);
stream_write_BYTE(pdu, SNDC_TRAINING); /* msgType */
stream_write_BYTE(pdu, 0); /* bPad */
stream_write_UINT16(pdu, 4); /* BodySize */
stream_write_UINT16(pdu, wTimeStamp);
stream_write_UINT16(pdu, wPackSize);
svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
}
/* server is getting a feel of the round trip time */
static void rdpsnd_process_message_training(rdpsndPlugin* rdpsnd, STREAM* data_in)
static void rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
{
UINT16 wTimeStamp;
UINT16 wPackSize;
STREAM* data_out;
stream_read_UINT16(data_in, wTimeStamp);
stream_read_UINT16(data_in, wPackSize);
stream_read_UINT16(s, wTimeStamp);
stream_read_UINT16(s, wPackSize);
data_out = stream_new(8);
stream_write_BYTE(data_out, SNDC_TRAINING); /* msgType */
stream_write_BYTE(data_out, 0); /* bPad */
stream_write_UINT16(data_out, 4); /* BodySize */
stream_write_UINT16(data_out, wTimeStamp);
stream_write_UINT16(data_out, wPackSize);
svc_plugin_send((rdpSvcPlugin*)rdpsnd, data_out);
rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
}
static void rdpsnd_process_message_wave_info(rdpsndPlugin* rdpsnd, STREAM* data_in, UINT16 BodySize)
static void rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, STREAM* s, UINT16 BodySize)
{
UINT16 wFormatNo;
AUDIO_FORMAT* format;
stream_read_UINT16(data_in, rdpsnd->wTimeStamp);
stream_read_UINT16(data_in, wFormatNo);
stream_read_BYTE(data_in, rdpsnd->cBlockNo);
stream_seek(data_in, 3); /* bPad */
stream_read(data_in, rdpsnd->waveData, 4);
rdpsnd->waveDataSize = BodySize - 8;
rdpsnd->wave_timestamp = get_mstime();
rdpsnd->expectingWave = TRUE;
DEBUG_SVC("waveDataSize %d wFormatNo %d", rdpsnd->waveDataSize, wFormatNo);
stream_read_UINT16(s, rdpsnd->wTimeStamp);
stream_read_UINT16(s, wFormatNo);
stream_read_BYTE(s, rdpsnd->cBlockNo);
stream_seek(s, 3); /* bPad */
stream_read(s, rdpsnd->waveData, 4);
rdpsnd->close_timestamp = 0;
rdpsnd->waveDataSize = BodySize - 8;
if (!rdpsnd->is_open)
format = &rdpsnd->ClientFormats[wFormatNo];
if (!rdpsnd->isOpen)
{
rdpsnd->current_format = wFormatNo;
rdpsnd->is_open = TRUE;
rdpsnd->isOpen = TRUE;
rdpsnd->wCurrentFormatNo = wFormatNo;
//rdpsnd_print_audio_format(format);
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->Open, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo],
rdpsnd->latency);
IFCALL(rdpsnd->device->Open, rdpsnd->device, format, rdpsnd->latency);
}
}
else if (wFormatNo != rdpsnd->current_format)
else if (wFormatNo != rdpsnd->wCurrentFormatNo)
{
rdpsnd->current_format = wFormatNo;
rdpsnd->wCurrentFormatNo = wFormatNo;
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->SetFormat, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo],
rdpsnd->latency);
IFCALL(rdpsnd->device->SetFormat, rdpsnd->device, format, rdpsnd->latency);
}
}
}
/* header is not removed from data in this function */
static void rdpsnd_process_message_wave(rdpsndPlugin* rdpsnd, STREAM* data_in)
void rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp, BYTE cConfirmedBlockNo)
{
UINT16 wTimeStamp;
UINT32 delay_ms;
UINT32 process_ms;
struct data_out_item* item;
STREAM* pdu;
rdpsnd->expectingWave = 0;
pdu = stream_new(8);
stream_write_BYTE(pdu, SNDC_WAVECONFIRM);
stream_write_BYTE(pdu, 0);
stream_write_UINT16(pdu, 4);
stream_write_UINT16(pdu, wTimeStamp);
stream_write_BYTE(pdu, cConfirmedBlockNo); /* cConfirmedBlockNo */
stream_write_BYTE(pdu, 0); /* bPad */
memcpy(stream_get_head(data_in), rdpsnd->waveData, 4);
svc_plugin_send((rdpSvcPlugin*) rdpsnd, pdu);
}
if (stream_get_size(data_in) != rdpsnd->waveDataSize)
void rdpsnd_device_send_wave_confirm_pdu(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
DEBUG_WARN("size error");
MessageQueue_Post(device->rdpsnd->queue, NULL, 0, (void*) wave, NULL);
}
static void rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
{
int size;
BYTE* data;
RDPSND_WAVE* wave;
AUDIO_FORMAT* format;
rdpsnd->expectingWave = FALSE;
/**
* The Wave PDU is a special case: it is always sent after a Wave Info PDU,
* and we do not process its header. Instead, the header is pad that needs
* to be filled with the first four bytes of the audio sample data sent as
* part of the preceding Wave Info PDU.
*/
CopyMemory(stream_get_head(s), rdpsnd->waveData, 4);
data = stream_get_head(s);
size = stream_get_size(s);
wave = (RDPSND_WAVE*) malloc(sizeof(RDPSND_WAVE));
wave->wLocalTimeA = GetTickCount();
wave->wTimeStampA = rdpsnd->wTimeStamp;
wave->wFormatNo = rdpsnd->wCurrentFormatNo;
wave->cBlockNo = rdpsnd->cBlockNo;
wave->data = data;
wave->length = size;
format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
wave->wAudioLength = rdpsnd_compute_audio_time_length(format, size);
if (!rdpsnd->device)
return;
}
if (rdpsnd->device)
if (rdpsnd->device->WaveDecode)
{
IFCALL(rdpsnd->device->Play, rdpsnd->device, stream_get_head(data_in), stream_get_size(data_in));
IFCALL(rdpsnd->device->WaveDecode, rdpsnd->device, wave);
}
process_ms = get_mstime() - rdpsnd->wave_timestamp;
delay_ms = 250;
wTimeStamp = rdpsnd->wTimeStamp + delay_ms;
DEBUG_SVC("data_size %d delay_ms %u process_ms %u",
stream_get_size(data_in), delay_ms, process_ms);
item = (struct data_out_item*) malloc(sizeof(struct data_out_item));
ZeroMemory(item, sizeof(struct data_out_item));
item->data_out = stream_new(8);
stream_write_BYTE(item->data_out, SNDC_WAVECONFIRM);
stream_write_BYTE(item->data_out, 0);
stream_write_UINT16(item->data_out, 4);
stream_write_UINT16(item->data_out, wTimeStamp);
stream_write_BYTE(item->data_out, rdpsnd->cBlockNo); /* cConfirmedBlockNo */
stream_write_BYTE(item->data_out, 0); /* bPad */
item->out_timestamp = rdpsnd->wave_timestamp + delay_ms;
list_enqueue(rdpsnd->data_out_list, item);
rdpsnd->plugin.interval_ms = 10;
if (rdpsnd->device->WavePlay)
{
IFCALL(rdpsnd->device->WavePlay, rdpsnd->device, wave);
}
else
{
IFCALL(rdpsnd->device->Play, rdpsnd->device, data, size);
}
static void rdpsnd_process_message_close(rdpsndPlugin* rdpsnd)
if (!rdpsnd->device->WavePlay)
{
wave->wTimeStampB = rdpsnd->wTimeStamp + wave->wAudioLength + TIME_DELAY_MS;
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + TIME_DELAY_MS;
rdpsnd->device->WaveConfirm(rdpsnd->device, wave);
}
}
static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
{
DEBUG_SVC("server closes.");
if (rdpsnd->device)
{
IFCALL(rdpsnd->device->Start, rdpsnd->device);
IFCALL(rdpsnd->device->Close, rdpsnd->device);
}
rdpsnd->close_timestamp = get_mstime() + 2000;
rdpsnd->plugin.interval_ms = 10;
rdpsnd->isOpen = FALSE;
}
static void rdpsnd_process_message_setvolume(rdpsndPlugin* rdpsnd, STREAM* data_in)
static void rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, STREAM* s)
{
UINT32 dwVolume;
stream_read_UINT32(data_in, dwVolume);
stream_read_UINT32(s, dwVolume);
DEBUG_SVC("dwVolume 0x%X", dwVolume);
if (rdpsnd->device)
@ -415,45 +569,45 @@ static void rdpsnd_process_message_setvolume(rdpsndPlugin* rdpsnd, STREAM* data_
}
}
static void rdpsnd_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
static void rdpsnd_recv_pdu(rdpSvcPlugin* plugin, STREAM* s)
{
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)plugin;
BYTE msgType;
UINT16 BodySize;
rdpsndPlugin* rdpsnd = (rdpsndPlugin*) plugin;
if (rdpsnd->expectingWave)
{
rdpsnd_process_message_wave(rdpsnd, data_in);
stream_free(data_in);
rdpsnd_recv_wave_pdu(rdpsnd, s);
stream_free(s);
return;
}
stream_read_BYTE(data_in, msgType); /* msgType */
stream_seek_BYTE(data_in); /* bPad */
stream_read_UINT16(data_in, BodySize);
stream_read_BYTE(s, msgType); /* msgType */
stream_seek_BYTE(s); /* bPad */
stream_read_UINT16(s, BodySize);
DEBUG_SVC("msgType %d BodySize %d", msgType, BodySize);
//printf("msgType %d BodySize %d\n", msgType, BodySize);
switch (msgType)
{
case SNDC_FORMATS:
rdpsnd_process_message_formats(rdpsnd, data_in);
rdpsnd_recv_server_audio_formats_pdu(rdpsnd, s);
break;
case SNDC_TRAINING:
rdpsnd_process_message_training(rdpsnd, data_in);
rdpsnd_recv_training_pdu(rdpsnd, s);
break;
case SNDC_WAVE:
rdpsnd_process_message_wave_info(rdpsnd, data_in, BodySize);
rdpsnd_recv_wave_info_pdu(rdpsnd, s, BodySize);
break;
case SNDC_CLOSE:
rdpsnd_process_message_close(rdpsnd);
rdpsnd_recv_close_pdu(rdpsnd);
break;
case SNDC_SETVOLUME:
rdpsnd_process_message_setvolume(rdpsnd, data_in);
rdpsnd_recv_volume_pdu(rdpsnd, s);
break;
default:
@ -461,7 +615,7 @@ static void rdpsnd_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
break;
}
stream_free(data_in);
stream_free(s);
}
static void rdpsnd_register_device_plugin(rdpsndPlugin* rdpsnd, rdpsndDevicePlugin* device)
@ -471,7 +625,11 @@ static void rdpsnd_register_device_plugin(rdpsndPlugin* rdpsnd, rdpsndDevicePlug
DEBUG_WARN("existing device, abort.");
return;
}
rdpsnd->device = device;
device->rdpsnd = rdpsnd;
device->WaveConfirm = rdpsnd_device_send_wave_confirm_pdu;
}
static BOOL rdpsnd_load_device_plugin(rdpsndPlugin* rdpsnd, const char* name, ADDIN_ARGV* args)
@ -481,7 +639,7 @@ static BOOL rdpsnd_load_device_plugin(rdpsndPlugin* rdpsnd, const char* name, AD
entry = (PFREERDP_RDPSND_DEVICE_ENTRY) freerdp_load_channel_addin_entry("rdpsnd", (LPSTR) name, NULL, 0);
if (entry == NULL)
if (!entry)
return FALSE;
entryPoints.rdpsnd = rdpsnd;
@ -554,15 +712,15 @@ static void rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, ADDIN_ARGV* args)
}
CommandLineSwitchCase(arg, "format")
{
rdpsnd->fixed_format = atoi(arg->Value);
rdpsnd->fixedFormat = atoi(arg->Value);
}
CommandLineSwitchCase(arg, "rate")
{
rdpsnd->fixed_rate = atoi(arg->Value);
rdpsnd->fixedRate = atoi(arg->Value);
}
CommandLineSwitchCase(arg, "channel")
{
rdpsnd->fixed_channel = atoi(arg->Value);
rdpsnd->fixedChannel = atoi(arg->Value);
}
CommandLineSwitchCase(arg, "latency")
{
@ -585,10 +743,9 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
DEBUG_SVC("connecting");
plugin->interval_callback = rdpsnd_process_interval;
rdpsnd->data_out_list = list_new();
rdpsnd->latency = -1;
rdpsnd->queue = MessageQueue_New();
rdpsnd->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, (void*) plugin, 0, NULL);
args = (ADDIN_ARGV*) plugin->channel_entry_points.pExtendedData;
@ -631,9 +788,10 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
if (rdpsnd->device == NULL)
if (!rdpsnd->device)
{
DEBUG_WARN("no sound device.");
return;
}
}
@ -644,21 +802,16 @@ static void rdpsnd_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
static void rdpsnd_process_terminate(rdpSvcPlugin* plugin)
{
struct data_out_item* item;
rdpsndPlugin* rdpsnd = (rdpsndPlugin*) plugin;
if (rdpsnd->device)
IFCALL(rdpsnd->device->Free, rdpsnd->device);
if (rdpsnd->data_out_list)
{
while ((item = list_dequeue(rdpsnd->data_out_list)) != NULL)
{
stream_free(item->data_out);
free(item);
}
list_free(rdpsnd->data_out_list);
}
MessageQueue_PostQuit(rdpsnd->queue, 0);
WaitForSingleObject(rdpsnd->thread, INFINITE);
MessageQueue_Free(rdpsnd->queue);
CloseHandle(rdpsnd->thread);
if (rdpsnd->subsystem)
free(rdpsnd->subsystem);
@ -666,9 +819,13 @@ static void rdpsnd_process_terminate(rdpSvcPlugin* plugin)
if (rdpsnd->device_name)
free(rdpsnd->device_name);
rdpsnd_free_supported_formats(rdpsnd);
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
rdpsnd->NumberOfServerFormats = 0;
rdpsnd->ServerFormats = NULL;
free(plugin);
rdpsnd_free_audio_formats(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
rdpsnd->NumberOfClientFormats = 0;
rdpsnd->ClientFormats = NULL;
}
/* rdpsnd is always built-in */
@ -688,10 +845,19 @@ int VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
strcpy(_p->plugin.channel_def.name, "rdpsnd");
_p->plugin.connect_callback = rdpsnd_process_connect;
_p->plugin.receive_callback = rdpsnd_process_receive;
_p->plugin.receive_callback = rdpsnd_recv_pdu;
_p->plugin.event_callback = rdpsnd_process_event;
_p->plugin.terminate_callback = rdpsnd_process_terminate;
#if !defined(_WIN32) && !defined(ANDROID)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGIO);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
}
#endif
svc_plugin_init((rdpSvcPlugin*) _p, pEntryPoints);
return 1;

View File

@ -22,29 +22,60 @@
#include <freerdp/channels/rdpsnd.h>
struct _RDPSND_WAVE
{
BYTE* data;
int length;
BYTE cBlockNo;
UINT16 wFormatNo;
UINT16 wTimeStampA;
UINT16 wTimeStampB;
UINT16 wLatency;
UINT16 wAudioLength;
UINT16 wPlaybackDelay;
UINT32 wLocalTimeA;
UINT32 wLocalTimeB;
};
typedef struct _RDPSND_WAVE RDPSND_WAVE;
typedef struct rdpsnd_plugin rdpsndPlugin;
typedef struct rdpsnd_device_plugin rdpsndDevicePlugin;
typedef BOOL (*pcFormatSupported) (rdpsndDevicePlugin* device, rdpsndFormat* format);
typedef void (*pcOpen) (rdpsndDevicePlugin* device, rdpsndFormat* format, int latency);
typedef void (*pcSetFormat) (rdpsndDevicePlugin* device, rdpsndFormat* format, int latency);
typedef BOOL (*pcFormatSupported) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format);
typedef void (*pcOpen) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency);
typedef void (*pcSetFormat) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency);
typedef UINT32 (*pcGetVolume) (rdpsndDevicePlugin* device);
typedef void (*pcSetVolume) (rdpsndDevicePlugin* device, UINT32 value);
typedef void (*pcPlay) (rdpsndDevicePlugin* device, BYTE* data, int size);
typedef void (*pcStart) (rdpsndDevicePlugin* device);
typedef void (*pcClose) (rdpsndDevicePlugin* device);
typedef void (*pcFree) (rdpsndDevicePlugin* device);
typedef void (*pcWaveDecode) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
typedef void (*pcWavePlay) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
typedef void (*pcWaveConfirm) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
struct rdpsnd_device_plugin
{
rdpsndPlugin* rdpsnd;
pcFormatSupported FormatSupported;
pcOpen Open;
pcSetFormat SetFormat;
pcGetVolume GetVolume;
pcSetVolume SetVolume;
pcPlay Play;
pcStart Start;
pcClose Close;
pcFree Free;
pcWaveDecode WaveDecode;
pcWavePlay WavePlay;
pcWaveConfirm WaveConfirm;
};
#define RDPSND_DEVICE_EXPORT_FUNC_NAME "freerdp_rdpsnd_client_subsystem_entry"

View File

@ -33,7 +33,7 @@
#include <winpr/cmdline.h>
#include <freerdp/types.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/svc_plugin.h>
#include "rdpsnd_main.h"
@ -63,7 +63,7 @@ struct rdpsnd_winmm_plugin
FREERDP_DSP_CONTEXT* dsp_context;
};
static BOOL rdpsnd_winmm_convert_format(const rdpsndFormat* in, WAVEFORMATEX* out)
static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out)
{
BOOL result = FALSE;
@ -71,6 +71,7 @@ static BOOL rdpsnd_winmm_convert_format(const rdpsndFormat* in, WAVEFORMATEX* ou
out->wFormatTag = WAVE_FORMAT_PCM;
out->nChannels = in->nChannels;
out->nSamplesPerSec = in->nSamplesPerSec;
switch (in->wFormatTag)
{
case WAVE_FORMAT_PCM:
@ -78,12 +79,13 @@ static BOOL rdpsnd_winmm_convert_format(const rdpsndFormat* in, WAVEFORMATEX* ou
result = TRUE;
break;
case 2: /* MS ADPCM */
case 0x11: /* IMA ADPCM */
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
out->wBitsPerSample = 16;
result = TRUE;
break;
}
out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
@ -98,19 +100,22 @@ static void rdpsnd_winmm_clear_datablocks(rdpsndWinmmPlugin* winmm, BOOL drain)
{
if (!drain && (datablock->header.dwFlags & WHDR_DONE) == 0)
break;
while ((datablock->header.dwFlags & WHDR_DONE) == 0)
WaitForSingleObject(winmm->event, INFINITE);
winmm->datablock_head = datablock->next;
free(datablock->header.lpData);
free(datablock);
}
}
static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
if (format != NULL)
if (format)
{
rdpsnd_winmm_convert_format(format, &winmm->format);
@ -121,7 +126,7 @@ static void rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, rdpsndFormat* fo
winmm->latency = latency;
}
static void rdpsnd_winmm_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
static void rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
MMRESULT result;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
@ -135,6 +140,7 @@ static void rdpsnd_winmm_open(rdpsndDevicePlugin* device, rdpsndFormat* format,
freerdp_dsp_context_reset_adpcm(winmm->dsp_context);
result = waveOutOpen(&winmm->out_handle, WAVE_MAPPER, &winmm->format, (DWORD_PTR) winmm->event, 0, CALLBACK_EVENT);
if (result != MMSYSERR_NOERROR)
{
DEBUG_WARN("waveOutOpen failed: %d", result);
@ -145,14 +151,16 @@ static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*)device;
if (winmm->out_handle != NULL)
if (winmm->out_handle)
{
DEBUG_SVC("close");
rdpsnd_winmm_clear_datablocks(winmm, TRUE);
if (waveOutClose(winmm->out_handle) != MMSYSERR_NOERROR)
{
DEBUG_WARN("waveOutClose error");
}
winmm->out_handle = NULL;
}
}
@ -167,7 +175,7 @@ static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
free(winmm);
}
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, rdpsndFormat* format)
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
{
MMRESULT result;
WAVEFORMATEX out;
@ -175,6 +183,7 @@ static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, rdpsndForm
if (rdpsnd_winmm_convert_format(format, &out))
{
result = waveOutOpen(NULL, WAVE_MAPPER, &out, 0, 0, WAVE_FORMAT_QUERY);
if (result == MMSYSERR_NOERROR)
return TRUE;
}
@ -182,11 +191,31 @@ static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, rdpsndForm
return FALSE;
}
static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
{
DWORD dwVolume;
UINT16 dwVolumeLeft;
UINT16 dwVolumeRight;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
if (!winmm->out_handle)
return dwVolume;
waveOutGetVolume(winmm->out_handle, &dwVolume);
return dwVolume;
}
static void rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (winmm->out_handle == NULL)
if (!winmm->out_handle)
return;
waveOutSetVolume(winmm->out_handle, value);
@ -200,17 +229,17 @@ static void rdpsnd_winmm_play(rdpsndDevicePlugin* device, BYTE* data, int size)
rdpsndWinmmDatablock* datablock;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (winmm->out_handle == NULL)
if (!winmm->out_handle)
return;
if (winmm->wformat == 2)
if (winmm->wformat == WAVE_FORMAT_ADPCM)
{
winmm->dsp_context->decode_ms_adpcm(winmm->dsp_context,
data, size, winmm->format.nChannels, winmm->block_size);
size = winmm->dsp_context->adpcm_size;
src = winmm->dsp_context->adpcm_buffer;
}
else if (winmm->wformat == 0x11)
else if (winmm->wformat == WAVE_FORMAT_DVI_ADPCM)
{
winmm->dsp_context->decode_ima_adpcm(winmm->dsp_context,
data, size, winmm->format.nChannels, winmm->block_size);
@ -223,25 +252,32 @@ static void rdpsnd_winmm_play(rdpsndDevicePlugin* device, BYTE* data, int size)
}
rdpsnd_winmm_clear_datablocks(winmm, FALSE);
for (last = winmm->datablock_head; last && last->next; last = last->next)
{
}
datablock = (rdpsndWinmmDatablock*) malloc(sizeof(rdpsndWinmmDatablock));
ZeroMemory(datablock, sizeof(rdpsndWinmmDatablock));
if (last)
last->next = datablock;
else
winmm->datablock_head = datablock;
datablock->header.dwBufferLength = size;
datablock->header.lpData = (LPSTR) malloc(size);
CopyMemory(datablock->header.lpData, src, size);
result = waveOutPrepareHeader(winmm->out_handle, &datablock->header, sizeof(datablock->header));
if (result != MMSYSERR_NOERROR)
{
DEBUG_WARN("waveOutPrepareHeader: %d", result);
return;
}
ResetEvent(winmm->event);
waveOutWrite(winmm->out_handle, &datablock->header, sizeof(datablock->header));
}
@ -272,6 +308,7 @@ int freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pE
winmm->device.Open = rdpsnd_winmm_open;
winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
winmm->device.SetFormat = rdpsnd_winmm_set_format;
winmm->device.GetVolume = rdpsnd_winmm_get_volume;
winmm->device.SetVolume = rdpsnd_winmm_set_volume;
winmm->device.Play = rdpsnd_winmm_play;
winmm->device.Start = rdpsnd_winmm_start;

View File

@ -27,7 +27,7 @@
#include <winpr/crt.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/stream.h>
#include <freerdp/utils/thread.h>
#include <freerdp/channels/wtsvc.h>
@ -129,8 +129,8 @@ static BOOL rdpsnd_server_recv_formats(rdpsnd_server* rdpsnd, STREAM* s)
if (rdpsnd->context.num_client_formats > 0)
{
rdpsnd->context.client_formats = (rdpsndFormat*) malloc(rdpsnd->context.num_client_formats * sizeof(rdpsndFormat));
ZeroMemory(rdpsnd->context.client_formats, sizeof(rdpsndFormat));
rdpsnd->context.client_formats = (AUDIO_FORMAT*) malloc(rdpsnd->context.num_client_formats * sizeof(AUDIO_FORMAT));
ZeroMemory(rdpsnd->context.client_formats, sizeof(AUDIO_FORMAT));
for (i = 0; i < rdpsnd->context.num_client_formats; i++)
{
@ -184,7 +184,8 @@ static void* rdpsnd_server_thread_func(void* arg)
while (1)
{
freerdp_thread_wait(thread);
if (freerdp_thread_wait(thread) < 0)
break;
if (freerdp_thread_is_stopped(thread))
break;
@ -254,7 +255,7 @@ static void rdpsnd_server_select_format(rdpsnd_server_context* context, int clie
{
int bs;
int out_buffer_size;
rdpsndFormat *format;
AUDIO_FORMAT *format;
rdpsnd_server* rdpsnd = (rdpsnd_server*) context;
if (client_format_index < 0 || client_format_index >= context->num_client_formats)
@ -308,7 +309,7 @@ static BOOL rdpsnd_server_send_audio_pdu(rdpsnd_server* rdpsnd)
BYTE* src;
int frames;
int fill_size;
rdpsndFormat* format;
AUDIO_FORMAT* format;
int tbytes_per_frame;
STREAM* s = rdpsnd->rdpsnd_pdu;

View File

@ -45,14 +45,8 @@ struct sample_plugin
rdpSvcPlugin plugin;
/* put your private data here */
};
static void sample_process_interval(rdpSvcPlugin* plugin)
{
printf("sample_process_interval:\n");
}
static void sample_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
{
int bytes;
@ -61,7 +55,7 @@ static void sample_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
printf("sample_process_receive:\n");
if (sample == NULL)
if (!sample)
{
printf("sample_process_receive: sample is nil\n");
return;
@ -72,6 +66,7 @@ static void sample_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
bytes = stream_get_size(data_in);
printf("sample_process_receive: got bytes %d\n", bytes);
if (bytes > 0)
{
data_out = stream_new(bytes);
@ -95,22 +90,15 @@ static void sample_process_connect(rdpSvcPlugin* plugin)
printf("sample_process_connect:\n");
if (sample == NULL)
{
if (!sample)
return;
}
/* if you want a call from channel thread once is a while do this */
plugin->interval_ms = 1000;
plugin->interval_callback = sample_process_interval;
}
static void sample_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
{
printf("sample_process_event:\n");
/* events comming from main freerdp window to plugin */
/* events coming from main freerdp window to plugin */
/* send them back with svc_plugin_send_event */
freerdp_event_free(event);
@ -122,10 +110,8 @@ static void sample_process_terminate(rdpSvcPlugin* plugin)
printf("sample_process_terminate:\n");
if (sample == NULL)
{
if (!sample)
return;
}
/* put your cleanup here */

View File

@ -341,7 +341,8 @@ static void* serial_thread_func(void* arg)
while (1)
{
freerdp_thread_wait_timeout(serial->thread, 500);
if (freerdp_thread_wait_timeout(serial->thread, 500) < 0)
break;
serial->nfds = 1;
FD_ZERO(&serial->read_fds);

View File

@ -35,7 +35,7 @@ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVER
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-utils)
MODULES freerdp-codec freerdp-utils)
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}

View File

@ -32,7 +32,7 @@
#include <alsa/asoundlib.h>
#include <freerdp/types.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/codec/dsp.h>
#include "tsmf_audio.h"

View File

@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto"
package="com.freerdp.afreerdp"
android:versionCode="1"
android:versionName="@FREERDP_VERSION_FULL@" >
<uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.android.vending.BILLING" />
<supports-screens android:anyDensity="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" />
<application android:name="com.freerdp.afreerdp.application.GlobalApp"
android:label="aFreeRDP"
android:icon="@drawable/icon_launcher_freerdp" >
<!-- Main activity -->
<activity android:name="com.freerdp.afreerdp.presentation.HomeActivity"
android:label="@string/app_title"
android:theme="@style/Theme.Main"
android:alwaysRetainTaskState="true"
>
<intent-filter android:label="@string/app_title">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Acra crash report dialog activity -->
<activity android:name="org.acra.CrashReportDialog"
android:theme="@android:style/Theme.Dialog" android:launchMode="singleInstance"
android:excludeFromRecents="true" android:finishOnTaskLaunch="true" />
<!-- Session request handler activity - used for search and internally to start sessions -->
<activity android:name="com.freerdp.afreerdp.services.SessionRequestHandlerActivity"
android:theme="@android:style/Theme.NoDisplay"
android:noHistory="true"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
<!-- Activity to create shortcuts -->
<activity android:name="com.freerdp.afreerdp.presentation.ShortcutsActivity"
android:theme="@style/Theme.Main"
android:label="@string/title_create_shortcut">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- It is recommended that you use an activity-alias to provide the "CREATE_SHORTCUT" -->
<!-- intent-filter. This gives you a way to set the text (and optionally the -->
<!-- icon) that will be seen in the launcher's create-shortcut user interface. -->
<activity-alias android:name="com.freerdp.afreerdp.presentation.CreateShortcuts"
android:targetActivity="com.freerdp.afreerdp.presentation.ShortcutsActivity"
android:label="@string/title_create_shortcut">
<!-- This intent-filter allows your shortcuts to be created in the launcher. -->
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
<activity android:name="com.freerdp.afreerdp.presentation.BookmarkActivity"
android:label="@string/title_bookmark_settings"
android:theme="@style/Theme.Settings">
</activity>
<activity android:name="com.freerdp.afreerdp.presentation.ApplicationSettingsActivity"
android:label="@string/title_application_settings"
android:theme="@style/Theme.Settings"
android:windowSoftInputMode="stateHidden">
</activity>
<activity android:name="com.freerdp.afreerdp.presentation.SessionActivity"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard"
android:windowSoftInputMode="adjustResize">
</activity>
<activity android:name="com.freerdp.afreerdp.presentation.AboutActivity"
android:label="@string/title_about"
android:theme="@style/Theme.Main">
</activity>
<activity android:name="com.freerdp.afreerdp.presentation.HelpActivity"
android:label="@string/title_help"
android:theme="@style/Theme.Main">
</activity>
<provider android:name="com.freerdp.afreerdp.services.FreeRDPSuggestionProvider"
android:authorities="com.freerdp.afreerdp.services.freerdpsuggestionprovider"
>
</provider>
<receiver android:name="com.freerdp.afreerdp.application.NetworkStateReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -1,7 +1,6 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# Android Client
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2013 Bernhard Miklautz <bmiklautz@thinstuff.at>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@ -16,23 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
set(ANDROID_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(ANDROID_PACKAGE_NAME "aFreeRDP")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake ${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml @ONLY)
if (ANDROID_SDK)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/local.properties.cmake ${CMAKE_CURRENT_SOURCE_DIR}/local.properties @ONLY)
endif()
add_subdirectory(jni)
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)")
endif()
# And isn't shiped with the android ndk/sdk so
# we need to find it on the local machine
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER, BOTH)
@ -45,20 +31,10 @@ if(ANDROID_BUILD_JAVA)
if(ANDROID_BUILD_JAVA_DEBUG)
set(ANDROID_BUILD_TYPE "debug")
set(APK "${ANDROID_SOURCE_DIR}/bin/${ANDROID_PACKAGE_NAME}-release-unsigned.apk")
else()
set(ANDROID_BUILD_TYPE "release")
set(APK "${ANDROID_SOURCE_DIR}/bin/${ANDROID_PACKAGE_NAME}-debug.apk")
endif()
endif(ANDROID_BUILD_JAVA)
# command to create the android package
add_custom_command(
OUTPUT "${APK}"
COMMAND ${ANT_COMMAND} ${ANDROID_BUILD_TYPE}
WORKING_DIRECTORY "${ANDROID_SOURCE_DIR}"
MAIN_DEPENDENCY AndroidManifest.xml
DEPENDS freerdp-android local.properties
)
add_custom_target(android-package ALL SOURCES "${APK}")
SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "gen;bin")
endif()
add_subdirectory(FreeRDPCore)
add_subdirectory(aFreeRDP)

View File

@ -0,0 +1,8 @@
<?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"/>
</classpath>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>FreeRDPCore</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto"
package="com.freerdp.freerdpcore"
android:versionCode="1"
android:versionName="@FREERDP_VERSION_FULL@" >
<uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<supports-screens android:anyDensity="true" android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" />
<application>
<!-- Activity to create shortcuts -->
<activity android:name=".presentation.ShortcutsActivity"
android:theme="@style/Theme.Main"
android:label="@string/title_create_shortcut">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- It is recommended that you use an activity-alias to provide the "CREATE_SHORTCUT" -->
<!-- intent-filter. This gives you a way to set the text (and optionally the -->
<!-- icon) that will be seen in the launcher's create-shortcut user interface. -->
<activity-alias android:name=".presentation.CreateShortcuts"
android:targetActivity="com.freerdp.freerdpcore.presentation.ShortcutsActivity"
android:label="@string/title_create_shortcut">
<!-- This intent-filter allows your shortcuts to be created in the launcher. -->
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
<activity android:name=".presentation.BookmarkActivity"
android:label="@string/title_bookmark_settings"
android:theme="@style/Theme.Settings">
<intent-filter>
<action android:name="freerdp.intent.action.BOOKMARK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="preferences"/>
</intent-filter>
</activity>
<activity android:name=".presentation.ApplicationSettingsActivity"
android:label="@string/title_application_settings"
android:theme="@style/Theme.Settings"
android:windowSoftInputMode="stateHidden">
</activity>
<activity android:name=".presentation.SessionActivity"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard"
android:windowSoftInputMode="adjustResize">
</activity>
<activity android:name=".presentation.AboutActivity"
android:label="@string/title_about"
android:theme="@style/Theme.Main">
</activity>
<activity android:name=".presentation.HelpActivity"
android:label="@string/title_help"
android:theme="@style/Theme.Main">
</activity>
<receiver android:name=".application.NetworkStateReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -0,0 +1,44 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# Android Client
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2013 Bernhard Miklautz <bmiklautz@thinstuff.at>
#
# 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.
set(ANDROID_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(ANDROID_PACKAGE_NAME "aFreeRDPCore")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake ${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml @ONLY)
if (ANDROID_SDK)
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/local.properties.cmake ${CMAKE_CURRENT_SOURCE_DIR}/local.properties @ONLY)
endif()
add_subdirectory(jni)
if(ANDROID_BUILD_JAVA)
set(ANDROIDLIB "${ANDROID_SOURCE_DIR}/bin/classes.jar")
# command to create the android package
add_custom_command(
OUTPUT "${ANDROIDLIB}"
COMMAND ${ANT_COMMAND} ${ANDROID_BUILD_TYPE}
WORKING_DIRECTORY "${ANDROID_SOURCE_DIR}"
MAIN_DEPENDENCY AndroidManifest.xml
DEPENDS freerdp-android local.properties
)
add_custom_target(android-lib ALL SOURCES "${ANDROIDLIB}")
SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "gen;bin")
endif()

View File

@ -0,0 +1,16 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked into Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="." default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View File

@ -24,6 +24,7 @@
#include <freerdp/utils/event.h>
#include <freerdp/constants.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/utils/file.h>
#include <android/bitmap.h>
#include <machine/cpu-features.h>
@ -536,7 +537,10 @@ JNIEXPORT void JNICALL jni_freerdp_set_data_directory(JNIEnv *env, jclass cls, j
const jbyte *directory = (*env)->GetStringUTFChars(env, jdirectory, NULL);
free(settings->HomePath);
free(settings->ConfigPath);
settings->HomePath = strdup(directory);
settings->ConfigPath = NULL;
freerdp_detect_paths(settings);
(*env)->ReleaseStringUTFChars(env, jdirectory, directory);
}
@ -655,7 +659,6 @@ JNIEXPORT void JNICALL jni_freerdp_set_performance_flags(
settings->FastPathOutput = TRUE;
settings->ColorDepth = 32;
settings->LargePointerFlag = TRUE;
settings->PerformanceFlags = PERF_FLAG_NONE;
settings->FrameMarkerCommandEnabled = TRUE;
}
@ -680,6 +683,7 @@ JNIEXPORT void JNICALL jni_freerdp_set_performance_flags(
/* Create performance flags from settings */
settings->PerformanceFlags = PERF_FLAG_NONE;
if (settings->AllowFontSmoothing)
settings->PerformanceFlags |= PERF_ENABLE_FONT_SMOOTHING;

View File

@ -20,32 +20,32 @@
#include "android_freerdp.h"
#include "android_freerdp_jni.h"
JNIEXPORT jint JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1new(JNIEnv *env, jclass cls)
JNIEXPORT jint JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1new(JNIEnv *env, jclass cls)
{
return jni_freerdp_new(env, cls);
}
JNIEXPORT void JNICALL JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1free(JNIEnv *env, jclass cls, jint instance)
JNIEXPORT void JNICALL JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1free(JNIEnv *env, jclass cls, jint instance)
{
jni_freerdp_free(env, cls, instance);
}
JNIEXPORT jboolean JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1connect(JNIEnv *env, jclass cls, jint instance)
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1connect(JNIEnv *env, jclass cls, jint instance)
{
return jni_freerdp_connect(env, cls, instance);
}
JNIEXPORT jboolean JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1disconnect(JNIEnv *env, jclass cls, jint instance)
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1disconnect(JNIEnv *env, jclass cls, jint instance)
{
return jni_freerdp_disconnect(env, cls, instance);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1cancel_1connection(JNIEnv *env, jclass cls, jint instance)
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1cancel_1connection(JNIEnv *env, jclass cls, jint instance)
{
jni_freerdp_cancel_connection(env, cls, instance);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1set_1connection_1info(JNIEnv *env, jclass cls, jint instance,
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1connection_1info(JNIEnv *env, jclass cls, jint instance,
jstring jhostname, jstring jusername, jstring jpassword, jstring jdomain, jint width, jint height, jint color_depth, jint port,
jboolean console, jint security, jstring certname)
{
@ -53,48 +53,48 @@ JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1se
width, height, color_depth, port, console, security, certname);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1set_1advanced_1settings(JNIEnv *env, jclass cls, jint instance, jstring remote_program, jstring work_dir)
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1advanced_1settings(JNIEnv *env, jclass cls, jint instance, jstring remote_program, jstring work_dir)
{
jni_freerdp_set_advanced_settings(env, cls, instance, remote_program, work_dir);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1set_1data_1directory(JNIEnv *env, jclass cls, jint instance, jstring directory)
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1data_1directory(JNIEnv *env, jclass cls, jint instance, jstring directory)
{
jni_freerdp_set_data_directory(env, cls, instance, directory);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1set_1performance_1flags(
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1performance_1flags(
JNIEnv *env, jclass cls, jint instance, jboolean remotefx, jboolean disableWallpaper, jboolean disableFullWindowDrag,
jboolean disableMenuAnimations, jboolean disableTheming, jboolean enableFontSmoothing, jboolean enableDesktopComposition)
{
jni_freerdp_set_performance_flags(env, cls, instance, remotefx, disableWallpaper, disableFullWindowDrag, disableMenuAnimations, disableTheming, enableFontSmoothing, enableDesktopComposition);
}
JNIEXPORT jboolean JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1update_1graphics(
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1update_1graphics(
JNIEnv *env, jclass cls, jint instance, jobject bitmap, jint x, jint y, jint width, jint height)
{
return jni_freerdp_update_graphics(env, cls, instance, bitmap, x, y, width, height);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1send_1cursor_1event(
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1cursor_1event(
JNIEnv *env, jclass cls, jint instance, jint x, jint y, jint flags)
{
jni_freerdp_send_cursor_event(env, cls, instance, x, y, flags);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1send_1key_1event(
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1key_1event(
JNIEnv *env, jclass cls, jint instance, jint keycode, jboolean down)
{
jni_freerdp_send_key_event(env, cls, instance, keycode, down);
}
JNIEXPORT void JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1send_1unicodekey_1event
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1unicodekey_1event
(JNIEnv *env, jclass cls, jint instance, jint keycode)
{
jni_freerdp_send_unicodekey_event(env, cls, instance, keycode);
}
JNIEXPORT jstring JNICALL Java_com_freerdp_afreerdp_services_LibFreeRDP_freerdp_1get_1version(JNIEnv *env, jclass cls)
JNIEXPORT jstring JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1version(JNIEnv *env, jclass cls)
{
return jni_freerdp_get_version(env, cls);
}

View File

@ -20,9 +20,9 @@
#ifndef __ANDROID_FREERDP_JNI_H
#define __ANDROID_FREERDP_JNI_H
#include "com_freerdp_afreerdp_services_LibFreeRDP.h"
#include "com_freerdp_freerdpcore_services_LibFreeRDP.h"
#define JAVA_LIBFREERDP_CLASS "com/freerdp/afreerdp/services/LibFreeRDP"
#define JAVA_LIBFREERDP_CLASS "com/freerdp/freerdpcore/services/LibFreeRDP"
#endif /* __ANDROID_FREERDP_JNI_H */

View File

@ -0,0 +1,125 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_freerdp_freerdpcore_services_LibFreeRDP */
#ifndef _Included_com_freerdp_freerdpcore_services_LibFreeRDP
#define _Included_com_freerdp_freerdpcore_services_LibFreeRDP
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_new
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1new
(JNIEnv *, jclass);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_free
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1free
(JNIEnv *, jclass, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_connect
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1connect
(JNIEnv *, jclass, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_disconnect
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1disconnect
(JNIEnv *, jclass, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_cancel_connection
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1cancel_1connection
(JNIEnv *, jclass, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_set_connection_info
* Signature: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIZILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1connection_1info
(JNIEnv *, jclass, jint, jstring, jstring, jstring, jstring, jint, jint, jint, jint, jboolean, jint, jstring);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_set_performance_flags
* Signature: (IZZZZZZZ)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1performance_1flags
(JNIEnv *, jclass, jint, jboolean, jboolean, jboolean, jboolean, jboolean, jboolean, jboolean);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_set_advanced_settings
* Signature: (ILjava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1advanced_1settings
(JNIEnv *, jclass, jint, jstring, jstring);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_set_data_directory
* Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1set_1data_1directory
(JNIEnv *, jclass, jint, jstring);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_update_graphics
* Signature: (ILandroid/graphics/Bitmap;IIII)Z
*/
JNIEXPORT jboolean JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1update_1graphics
(JNIEnv *, jclass, jint, jobject, jint, jint, jint, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_send_cursor_event
* Signature: (IIII)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1cursor_1event
(JNIEnv *, jclass, jint, jint, jint, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_send_key_event
* Signature: (IIZ)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1key_1event
(JNIEnv *, jclass, jint, jint, jboolean);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_send_unicodekey_event
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1send_1unicodekey_1event
(JNIEnv *, jclass, jint, jint);
/*
* Class: com_freerdp_freerdpcore_services_LibFreeRDP
* Method: freerdp_get_version
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_freerdp_freerdpcore_services_LibFreeRDP_freerdp_1get_1version
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,15 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-8
android.library=true

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 301 B

After

Width:  |  Height:  |  Size: 301 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 771 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 237 B

After

Width:  |  Height:  |  Size: 237 B

View File

Before

Width:  |  Height:  |  Size: 892 B

After

Width:  |  Height:  |  Size: 892 B

View File

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 572 B

View File

Before

Width:  |  Height:  |  Size: 930 B

After

Width:  |  Height:  |  Size: 930 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 293 B

After

Width:  |  Height:  |  Size: 293 B

Some files were not shown because too many files have changed in this diff Show More