merged upstream
1
.gitignore
vendored
@ -41,6 +41,7 @@ client/X11/xfreerdp.1
|
||||
# Mac OS X
|
||||
.DS_Store
|
||||
*.xcodeproj/
|
||||
DerivedData/
|
||||
|
||||
# Windows
|
||||
*.vcxproj
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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})
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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>
|
@ -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)
|
||||
|
8
client/Android/FreeRDPCore/.classpath
Normal 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>
|
33
client/Android/FreeRDPCore/.project
Normal 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>
|
73
client/Android/FreeRDPCore/AndroidManifest.xml.cmake
Normal 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>
|
44
client/Android/FreeRDPCore/CMakeLists.txt
Normal 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()
|
16
client/Android/FreeRDPCore/ant.properties
Normal 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.
|
92
client/Android/FreeRDPCore/build.xml
Normal 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>
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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 */
|
||||
|
@ -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
|
15
client/Android/FreeRDPCore/project.properties
Normal 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
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 301 B |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 838 B After Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 771 B After Width: | Height: | Size: 771 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 237 B |
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 892 B |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 506 B |
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 930 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 293 B After Width: | Height: | Size: 293 B |