From 23a8354656a9048bf25116abd98a05c45b2133c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Sun, 18 Aug 2013 21:52:55 -0400 Subject: [PATCH] channels: rdpsnd refactoring, drdynvc initial server-side code --- channels/drdynvc/CMakeLists.txt | 3 + channels/drdynvc/ChannelOptions.cmake | 2 +- channels/drdynvc/server/CMakeLists.txt | 35 ++ channels/drdynvc/server/drdynvc_main.c | 149 +++++++ channels/drdynvc/server/drdynvc_main.h | 37 ++ channels/rdpsnd/server/CMakeLists.txt | 3 +- channels/rdpsnd/server/rdpsnd.c | 560 ------------------------ channels/rdpsnd/server/rdpsnd_main.c | 572 +++++++++++++++++++++++++ channels/rdpsnd/server/rdpsnd_main.h | 48 +++ channels/server/channels.c | 4 + include/freerdp/server/drdynvc.h | 50 +++ include/freerdp/server/rdpsnd.h | 26 +- server/Mac/mf_interface.h | 2 +- server/Mac/mf_rdpsnd.c | 2 +- server/Mac/mf_rdpsnd.h | 2 +- server/Sample/sf_rdpsnd.c | 2 +- server/Sample/sfreerdp.h | 2 +- server/Windows/wf_directsound.c | 2 +- server/Windows/wf_directsound.h | 2 +- server/Windows/wf_interface.h | 2 +- server/Windows/wf_rdpsnd.c | 2 +- server/Windows/wf_wasapi.c | 2 +- server/Windows/wf_wasapi.h | 2 +- 23 files changed, 930 insertions(+), 581 deletions(-) create mode 100644 channels/drdynvc/server/CMakeLists.txt create mode 100644 channels/drdynvc/server/drdynvc_main.c create mode 100644 channels/drdynvc/server/drdynvc_main.h delete mode 100644 channels/rdpsnd/server/rdpsnd.c create mode 100644 channels/rdpsnd/server/rdpsnd_main.c create mode 100644 channels/rdpsnd/server/rdpsnd_main.h create mode 100644 include/freerdp/server/drdynvc.h diff --git a/channels/drdynvc/CMakeLists.txt b/channels/drdynvc/CMakeLists.txt index 8fc41cb90..9a6ee1ff6 100644 --- a/channels/drdynvc/CMakeLists.txt +++ b/channels/drdynvc/CMakeLists.txt @@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS) add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) endif() +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/drdynvc/ChannelOptions.cmake b/channels/drdynvc/ChannelOptions.cmake index 1b226e64d..76376b6bf 100644 --- a/channels/drdynvc/ChannelOptions.cmake +++ b/channels/drdynvc/ChannelOptions.cmake @@ -1,7 +1,7 @@ set(OPTION_DEFAULT OFF) set(OPTION_CLIENT_DEFAULT ON) -set(OPTION_SERVER_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) define_channel_options(NAME "drdynvc" TYPE "static" DESCRIPTION "Dynamic Virtual Channel Extension" diff --git a/channels/drdynvc/server/CMakeLists.txt b/channels/drdynvc/server/CMakeLists.txt new file mode 100644 index 000000000..8b7c27d25 --- /dev/null +++ b/channels/drdynvc/server/CMakeLists.txt @@ -0,0 +1,35 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 Marc-Andre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("drdynvc") + +set(${MODULE_PREFIX}_SRCS + drdynvc_main.c + drdynvc_main.h) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry") + +set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE freerdp + MODULES freerdp-utils) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/drdynvc/server/drdynvc_main.c b/channels/drdynvc/server/drdynvc_main.c new file mode 100644 index 000000000..2897023d4 --- /dev/null +++ b/channels/drdynvc/server/drdynvc_main.c @@ -0,0 +1,149 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Dynamic Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "drdynvc_main.h" + +static void* drdynvc_server_thread(void* arg) +{ + wStream* s; + DWORD status; + DWORD nCount; + void* buffer; + HANDLE events[8]; + HANDLE ChannelEvent; + UINT32 BytesReturned; + DrdynvcServerContext* context; + + context = (DrdynvcServerContext*) arg; + + buffer = NULL; + BytesReturned = 0; + ChannelEvent = NULL; + + s = Stream_New(NULL, 4096); + + if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + nCount = 0; + events[nCount++] = ChannelEvent; + events[nCount++] = context->priv->StopEvent; + + while (1) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0) + { + break; + } + + if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, + Stream_Buffer(s), Stream_Capacity(s), &BytesReturned)) + { + if (BytesReturned) + Stream_Seek(s, BytesReturned); + } + else + { + Stream_EnsureRemainingCapacity(s, BytesReturned); + } + } + + Stream_Free(s, TRUE); + + return NULL; +} + +static int drdynvc_server_start(DrdynvcServerContext* context) +{ + context->priv->ChannelHandle = WTSVirtualChannelOpenEx(context->vcm, "rdpdr", 0); + + if (!context->priv->ChannelHandle) + return -1; + + context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + context->priv->Thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) drdynvc_server_thread, (void*) context, 0, NULL); + + return 0; +} + +static int drdynvc_server_stop(DrdynvcServerContext* context) +{ + SetEvent(context->priv->StopEvent); + + WaitForSingleObject(context->priv->Thread, INFINITE); + CloseHandle(context->priv->Thread); + + return 0; +} + +DrdynvcServerContext* drdynvc_server_context_new(WTSVirtualChannelManager* vcm) +{ + DrdynvcServerContext* context; + + context = (DrdynvcServerContext*) malloc(sizeof(DrdynvcServerContext)); + + if (context) + { + ZeroMemory(context, sizeof(DrdynvcServerContext)); + + context->vcm = vcm; + + context->Start = drdynvc_server_start; + context->Stop = drdynvc_server_stop; + + context->priv = (DrdynvcServerPrivate*) malloc(sizeof(DrdynvcServerPrivate)); + + if (context->priv) + { + ZeroMemory(context->priv, sizeof(DrdynvcServerPrivate)); + } + } + + return context; +} + +void drdynvc_server_context_free(DrdynvcServerContext* context) +{ + if (context) + { + if (context->priv) + { + free(context->priv); + } + + free(context); + } +} diff --git a/channels/drdynvc/server/drdynvc_main.h b/channels/drdynvc/server/drdynvc_main.h new file mode 100644 index 000000000..aa69b5706 --- /dev/null +++ b/channels/drdynvc/server/drdynvc_main.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Dynamic Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H +#define FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H + +#include +#include +#include + +#include +#include + +struct _drdynvc_server_private +{ + HANDLE Thread; + HANDLE StopEvent; + void* ChannelHandle; +}; + +#endif /* FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H */ diff --git a/channels/rdpsnd/server/CMakeLists.txt b/channels/rdpsnd/server/CMakeLists.txt index b6d0c0901..c98816d68 100644 --- a/channels/rdpsnd/server/CMakeLists.txt +++ b/channels/rdpsnd/server/CMakeLists.txt @@ -18,7 +18,8 @@ define_channel_server("rdpsnd") set(${MODULE_PREFIX}_SRCS - rdpsnd.c) + rdpsnd_main.c + rdpsnd_main.h) add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry") diff --git a/channels/rdpsnd/server/rdpsnd.c b/channels/rdpsnd/server/rdpsnd.c deleted file mode 100644 index 57a9593a2..000000000 --- a/channels/rdpsnd/server/rdpsnd.c +++ /dev/null @@ -1,560 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * Server Audio Virtual Channel - * - * Copyright 2012 Vic Lee - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -typedef struct _rdpsnd_server -{ - rdpsnd_server_context context; - - HANDLE thread; - HANDLE StopEvent; - void* rdpsnd_channel; - wStream* rdpsnd_pdu; - - FREERDP_DSP_CONTEXT* dsp_context; - BYTE* out_buffer; - int out_buffer_size; - int out_frames; - int out_pending_frames; - - UINT32 src_bytes_per_sample; - UINT32 src_bytes_per_frame; -} rdpsnd_server; - - -static BOOL rdpsnd_server_send_formats(rdpsnd_server* rdpsnd, wStream* s) -{ - int pos; - UINT16 i; - BOOL status; - - Stream_Write_UINT8(s, SNDC_FORMATS); - Stream_Write_UINT8(s, 0); - Stream_Seek_UINT16(s); - - Stream_Write_UINT32(s, 0); /* dwFlags */ - Stream_Write_UINT32(s, 0); /* dwVolume */ - Stream_Write_UINT32(s, 0); /* dwPitch */ - Stream_Write_UINT16(s, 0); /* wDGramPort */ - Stream_Write_UINT16(s, rdpsnd->context.num_server_formats); /* wNumberOfFormats */ - Stream_Write_UINT8(s, rdpsnd->context.block_no); /* cLastBlockConfirmed */ - Stream_Write_UINT16(s, 0x06); /* wVersion */ - Stream_Write_UINT8(s, 0); /* bPad */ - - for (i = 0; i < rdpsnd->context.num_server_formats; i++) - { - Stream_Write_UINT16(s, rdpsnd->context.server_formats[i].wFormatTag); /* wFormatTag (WAVE_FORMAT_PCM) */ - Stream_Write_UINT16(s, rdpsnd->context.server_formats[i].nChannels); /* nChannels */ - Stream_Write_UINT32(s, rdpsnd->context.server_formats[i].nSamplesPerSec); /* nSamplesPerSec */ - - Stream_Write_UINT32(s, rdpsnd->context.server_formats[i].nSamplesPerSec * - rdpsnd->context.server_formats[i].nChannels * - rdpsnd->context.server_formats[i].wBitsPerSample / 8); /* nAvgBytesPerSec */ - - Stream_Write_UINT16(s, rdpsnd->context.server_formats[i].nBlockAlign); /* nBlockAlign */ - Stream_Write_UINT16(s, rdpsnd->context.server_formats[i].wBitsPerSample); /* wBitsPerSample */ - Stream_Write_UINT16(s, rdpsnd->context.server_formats[i].cbSize); /* cbSize */ - - if (rdpsnd->context.server_formats[i].cbSize > 0) - { - Stream_Write(s, rdpsnd->context.server_formats[i].data, rdpsnd->context.server_formats[i].cbSize); - } - } - - pos = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, pos - 4); - Stream_SetPosition(s, pos); - status = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, Stream_Buffer(s), Stream_GetPosition(s), NULL); - Stream_SetPosition(s, 0); - - return status; -} - -static void rdpsnd_server_recv_waveconfirm(rdpsnd_server* rdpsnd, wStream* s) -{ - //unhandled for now - - UINT16 timestamp = 0; - BYTE confirmBlockNum = 0; - Stream_Read_UINT16(s, timestamp); - Stream_Read_UINT8(s, confirmBlockNum); - Stream_Seek_UINT8(s); // padding -} - -static void rdpsnd_server_recv_quality_mode(rdpsnd_server* rdpsnd, wStream* s) -{ - //unhandled for now - UINT16 quality; - - Stream_Read_UINT16(s, quality); - Stream_Seek_UINT16(s); // reserved - - fprintf(stderr, "Client requested sound quality: %#0X\n", quality); -} - -static BOOL rdpsnd_server_recv_formats(rdpsnd_server* rdpsnd, wStream* s) -{ - int i, num_known_format = 0; - UINT32 flags, vol, pitch; - UINT16 udpPort, version; - BYTE lastblock; - - - Stream_Read_UINT32(s, flags); /* dwFlags */ - Stream_Read_UINT32(s, vol); /* dwVolume */ - Stream_Read_UINT32(s, pitch); /* dwPitch */ - Stream_Read_UINT16(s, udpPort); /* wDGramPort */ - Stream_Read_UINT16(s, rdpsnd->context.num_client_formats); /* wNumberOfFormats */ - Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ - Stream_Read_UINT16(s, version); /* wVersion */ - Stream_Seek_UINT8(s); /* bPad */ - - if (rdpsnd->context.num_client_formats > 0) - { - 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++) - { - Stream_Read_UINT16(s, rdpsnd->context.client_formats[i].wFormatTag); - Stream_Read_UINT16(s, rdpsnd->context.client_formats[i].nChannels); - Stream_Read_UINT32(s, rdpsnd->context.client_formats[i].nSamplesPerSec); - Stream_Read_UINT32(s, rdpsnd->context.client_formats[i].nAvgBytesPerSec); - Stream_Read_UINT16(s, rdpsnd->context.client_formats[i].nBlockAlign); - Stream_Read_UINT16(s, rdpsnd->context.client_formats[i].wBitsPerSample); - Stream_Read_UINT16(s, rdpsnd->context.client_formats[i].cbSize); - - if (rdpsnd->context.client_formats[i].cbSize > 0) - { - Stream_Seek(s, rdpsnd->context.client_formats[i].cbSize); - } - - if (rdpsnd->context.client_formats[i].wFormatTag != 0) - { - //lets call this a known format - //TODO: actually look through our own list of known formats - num_known_format++; - } - } - } - - if (num_known_format == 0) - { - fprintf(stderr, "Client doesnt support any known formats!\n"); - return FALSE; - } - - return TRUE; -} - -static void* rdpsnd_server_thread_func(void* arg) -{ - void* fd; - wStream* s; - void* buffer; - DWORD status; - BYTE msgType; - UINT16 BodySize; - HANDLE events[2]; - UINT32 bytes_returned = 0; - rdpsnd_server* rdpsnd = (rdpsnd_server*) arg; - - events[0] = rdpsnd->StopEvent; - - if (WTSVirtualChannelQuery(rdpsnd->rdpsnd_channel, WTSVirtualFileHandle, &buffer, &bytes_returned) == TRUE) - { - fd = *((void**) buffer); - WTSFreeMemory(buffer); - - events[1] = CreateWaitObjectEvent(NULL, TRUE, FALSE, fd); - } - - s = Stream_New(NULL, 4096); - - rdpsnd_server_send_formats(rdpsnd, s); - - while (1) - { - status = WaitForMultipleObjects(2, events, FALSE, INFINITE); - - if (WaitForSingleObject(rdpsnd->StopEvent, 0) == WAIT_OBJECT_0) - { - break; - } - - Stream_SetPosition(s, 0); - - if (WTSVirtualChannelRead(rdpsnd->rdpsnd_channel, 0, Stream_Buffer(s), - Stream_Capacity(s), &bytes_returned) == FALSE) - { - if (bytes_returned == 0) - break; - - Stream_EnsureRemainingCapacity(s, (int) bytes_returned); - - if (WTSVirtualChannelRead(rdpsnd->rdpsnd_channel, 0, Stream_Buffer(s), - Stream_Capacity(s), &bytes_returned) == FALSE) - break; - } - - Stream_Read_UINT8(s, msgType); - Stream_Seek_UINT8(s); /* bPad */ - Stream_Read_UINT16(s, BodySize); - - switch (msgType) - { - case SNDC_WAVECONFIRM: - rdpsnd_server_recv_waveconfirm(rdpsnd, s); - break; - - case SNDC_QUALITYMODE: - rdpsnd_server_recv_quality_mode(rdpsnd, s); - break; - case SNDC_FORMATS: - if (rdpsnd_server_recv_formats(rdpsnd, s)) - { - IFCALL(rdpsnd->context.Activated, &rdpsnd->context); - } - break; - default: - fprintf(stderr, "UNKOWN MESSAGE TYPE!! (%#0X)\n\n", msgType); - break; - } - } - - Stream_Free(s, TRUE); - - return NULL; -} - -static BOOL rdpsnd_server_initialize(rdpsnd_server_context* context) -{ - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - - rdpsnd->rdpsnd_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpsnd", 0); - - if (rdpsnd->rdpsnd_channel != NULL) - { - rdpsnd->rdpsnd_pdu = Stream_New(NULL, 4096); - - rdpsnd->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - rdpsnd->thread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) rdpsnd_server_thread_func, (void*) rdpsnd, 0, NULL); - - return TRUE; - } - else - { - return FALSE; - } -} - -static void rdpsnd_server_select_format(rdpsnd_server_context* context, int client_format_index) -{ - int bs; - int out_buffer_size; - AUDIO_FORMAT *format; - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - - if (client_format_index < 0 || client_format_index >= context->num_client_formats) - { - fprintf(stderr, "rdpsnd_server_select_format: index %d is not correct.\n", client_format_index); - return; - } - - rdpsnd->src_bytes_per_sample = context->src_format.wBitsPerSample / 8; - rdpsnd->src_bytes_per_frame = rdpsnd->src_bytes_per_sample * context->src_format.nChannels; - - context->selected_client_format = client_format_index; - format = &context->client_formats[client_format_index]; - - if (format->nSamplesPerSec == 0) - { - fprintf(stderr, "Invalid Client Sound Format!!\n\n"); - return; - } - - if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM) - { - bs = (format->nBlockAlign - 4 * format->nChannels) * 4; - rdpsnd->out_frames = (format->nBlockAlign * 4 * format->nChannels * 2 / bs + 1) * bs / (format->nChannels * 2); - } - else if (format->wFormatTag == WAVE_FORMAT_ADPCM) - { - bs = (format->nBlockAlign - 7 * format->nChannels) * 2 / format->nChannels + 2; - rdpsnd->out_frames = bs * 4; - } - else - { - rdpsnd->out_frames = 0x4000 / rdpsnd->src_bytes_per_frame; - } - - if (format->nSamplesPerSec != context->src_format.nSamplesPerSec) - { - rdpsnd->out_frames = (rdpsnd->out_frames * context->src_format.nSamplesPerSec + format->nSamplesPerSec - 100) / format->nSamplesPerSec; - } - rdpsnd->out_pending_frames = 0; - - out_buffer_size = rdpsnd->out_frames * rdpsnd->src_bytes_per_frame; - - if (rdpsnd->out_buffer_size < out_buffer_size) - { - rdpsnd->out_buffer = (BYTE*) realloc(rdpsnd->out_buffer, out_buffer_size); - rdpsnd->out_buffer_size = out_buffer_size; - } - - freerdp_dsp_context_reset_adpcm(rdpsnd->dsp_context); -} - -static BOOL rdpsnd_server_send_audio_pdu(rdpsnd_server* rdpsnd) -{ - int size; - BYTE* src; - int frames; - int fill_size; - BOOL status; - AUDIO_FORMAT* format; - int tbytes_per_frame; - wStream* s = rdpsnd->rdpsnd_pdu; - - format = &rdpsnd->context.client_formats[rdpsnd->context.selected_client_format]; - tbytes_per_frame = format->nChannels * rdpsnd->src_bytes_per_sample; - - if ((format->nSamplesPerSec == rdpsnd->context.src_format.nSamplesPerSec) && - (format->nChannels == rdpsnd->context.src_format.nChannels)) - { - src = rdpsnd->out_buffer; - frames = rdpsnd->out_pending_frames; - } - else - { - rdpsnd->dsp_context->resample(rdpsnd->dsp_context, rdpsnd->out_buffer, rdpsnd->src_bytes_per_sample, - rdpsnd->context.src_format.nChannels, rdpsnd->context.src_format.nSamplesPerSec, rdpsnd->out_pending_frames, - format->nChannels, format->nSamplesPerSec); - frames = rdpsnd->dsp_context->resampled_frames; - src = rdpsnd->dsp_context->resampled_buffer; - } - size = frames * tbytes_per_frame; - - if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM) - { - rdpsnd->dsp_context->encode_ima_adpcm(rdpsnd->dsp_context, - src, size, format->nChannels, format->nBlockAlign); - src = rdpsnd->dsp_context->adpcm_buffer; - size = rdpsnd->dsp_context->adpcm_size; - } - else if (format->wFormatTag == WAVE_FORMAT_ADPCM) - { - rdpsnd->dsp_context->encode_ms_adpcm(rdpsnd->dsp_context, - src, size, format->nChannels, format->nBlockAlign); - src = rdpsnd->dsp_context->adpcm_buffer; - size = rdpsnd->dsp_context->adpcm_size; - } - - rdpsnd->context.block_no = (rdpsnd->context.block_no + 1) % 256; - - /* Fill to nBlockAlign for the last audio packet */ - - fill_size = 0; - - if ((format->wFormatTag == WAVE_FORMAT_DVI_ADPCM || format->wFormatTag == WAVE_FORMAT_ADPCM) && - (rdpsnd->out_pending_frames < rdpsnd->out_frames) && ((size % format->nBlockAlign) != 0)) - { - fill_size = format->nBlockAlign - (size % format->nBlockAlign); - } - - /* WaveInfo PDU */ - Stream_SetPosition(s, 0); - Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */ - Stream_Write_UINT8(s, 0); /* bPad */ - Stream_Write_UINT16(s, size + fill_size + 8); /* BodySize */ - - Stream_Write_UINT16(s, 0); /* wTimeStamp */ - Stream_Write_UINT16(s, rdpsnd->context.selected_client_format); /* wFormatNo */ - Stream_Write_UINT8(s, rdpsnd->context.block_no); /* cBlockNo */ - Stream_Seek(s, 3); /* bPad */ - Stream_Write(s, src, 4); - - WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, Stream_Buffer(s), Stream_GetPosition(s), NULL); - Stream_SetPosition(s, 0); - - /* Wave PDU */ - Stream_EnsureRemainingCapacity(s, size + fill_size); - Stream_Write_UINT32(s, 0); /* bPad */ - Stream_Write(s, src + 4, size - 4); - - if (fill_size > 0) - Stream_Zero(s, fill_size); - - status = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, Stream_Buffer(s), Stream_GetPosition(s), NULL); - Stream_SetPosition(s, 0); - - rdpsnd->out_pending_frames = 0; - - return status; -} - -static BOOL rdpsnd_server_send_samples(rdpsnd_server_context* context, const void* buf, int nframes) -{ - int cframes; - int cframesize; - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - - if (rdpsnd->context.selected_client_format < 0) - return FALSE; - - while (nframes > 0) - { - cframes = MIN(nframes, rdpsnd->out_frames - rdpsnd->out_pending_frames); - cframesize = cframes * rdpsnd->src_bytes_per_frame; - - CopyMemory(rdpsnd->out_buffer + (rdpsnd->out_pending_frames * rdpsnd->src_bytes_per_frame), buf, cframesize); - buf = (BYTE*) buf + cframesize; - nframes -= cframes; - rdpsnd->out_pending_frames += cframes; - - if (rdpsnd->out_pending_frames >= rdpsnd->out_frames) - { - if (!rdpsnd_server_send_audio_pdu(rdpsnd)) - return FALSE; - } - } - - return TRUE; -} - -static BOOL rdpsnd_server_set_volume(rdpsnd_server_context* context, int left, int right) -{ - int pos; - BOOL status; - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - wStream* s = rdpsnd->rdpsnd_pdu; - - Stream_Write_UINT8(s, SNDC_SETVOLUME); - Stream_Write_UINT8(s, 0); - Stream_Seek_UINT16(s); - - Stream_Write_UINT16(s, left); - Stream_Write_UINT16(s, right); - - pos = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, pos - 4); - Stream_SetPosition(s, pos); - status = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, Stream_Buffer(s), Stream_GetPosition(s), NULL); - Stream_SetPosition(s, 0); - - return status; -} - -static BOOL rdpsnd_server_close(rdpsnd_server_context* context) -{ - int pos; - BOOL status; - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - wStream* s = rdpsnd->rdpsnd_pdu; - - if (rdpsnd->context.selected_client_format < 0) - return FALSE; - - if (rdpsnd->out_pending_frames > 0) - { - if (!rdpsnd_server_send_audio_pdu(rdpsnd)) - return FALSE; - } - - rdpsnd->context.selected_client_format = -1; - - Stream_Write_UINT8(s, SNDC_CLOSE); - Stream_Write_UINT8(s, 0); - Stream_Seek_UINT16(s); - - pos = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, pos - 4); - Stream_SetPosition(s, pos); - status = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, Stream_Buffer(s), Stream_GetPosition(s), NULL); - Stream_SetPosition(s, 0); - - return status; -} - -rdpsnd_server_context* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm) -{ - rdpsnd_server* rdpsnd; - - rdpsnd = (rdpsnd_server*) malloc(sizeof(rdpsnd_server)); - ZeroMemory(rdpsnd, sizeof(rdpsnd_server)); - - rdpsnd->context.vcm = vcm; - rdpsnd->context.selected_client_format = -1; - rdpsnd->context.Initialize = rdpsnd_server_initialize; - rdpsnd->context.SelectFormat = rdpsnd_server_select_format; - rdpsnd->context.SendSamples = rdpsnd_server_send_samples; - rdpsnd->context.SetVolume = rdpsnd_server_set_volume; - rdpsnd->context.Close = rdpsnd_server_close; - - rdpsnd->dsp_context = freerdp_dsp_context_new(); - - return (rdpsnd_server_context*) rdpsnd; -} - -void rdpsnd_server_context_free(rdpsnd_server_context* context) -{ - rdpsnd_server* rdpsnd = (rdpsnd_server*) context; - - SetEvent(rdpsnd->StopEvent); - WaitForSingleObject(rdpsnd->thread, INFINITE); - - if (rdpsnd->rdpsnd_channel) - WTSVirtualChannelClose(rdpsnd->rdpsnd_channel); - - if (rdpsnd->rdpsnd_pdu) - Stream_Free(rdpsnd->rdpsnd_pdu, TRUE); - - if (rdpsnd->out_buffer) - free(rdpsnd->out_buffer); - - if (rdpsnd->dsp_context) - freerdp_dsp_context_free(rdpsnd->dsp_context); - - if (rdpsnd->context.client_formats) - free(rdpsnd->context.client_formats); - - free(rdpsnd); -} diff --git a/channels/rdpsnd/server/rdpsnd_main.c b/channels/rdpsnd/server/rdpsnd_main.c new file mode 100644 index 000000000..d56de7fee --- /dev/null +++ b/channels/rdpsnd/server/rdpsnd_main.c @@ -0,0 +1,572 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Server Audio Virtual Channel + * + * Copyright 2012 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "rdpsnd_main.h" + +static BOOL rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) +{ + int pos; + UINT16 i; + BOOL status; + + Stream_Write_UINT8(s, SNDC_FORMATS); + Stream_Write_UINT8(s, 0); + Stream_Seek_UINT16(s); + + Stream_Write_UINT32(s, 0); /* dwFlags */ + Stream_Write_UINT32(s, 0); /* dwVolume */ + Stream_Write_UINT32(s, 0); /* dwPitch */ + Stream_Write_UINT16(s, 0); /* wDGramPort */ + Stream_Write_UINT16(s, context->num_server_formats); /* wNumberOfFormats */ + Stream_Write_UINT8(s, context->block_no); /* cLastBlockConfirmed */ + Stream_Write_UINT16(s, 0x06); /* wVersion */ + Stream_Write_UINT8(s, 0); /* bPad */ + + for (i = 0; i < context->num_server_formats; i++) + { + Stream_Write_UINT16(s, context->server_formats[i].wFormatTag); /* wFormatTag (WAVE_FORMAT_PCM) */ + Stream_Write_UINT16(s, context->server_formats[i].nChannels); /* nChannels */ + Stream_Write_UINT32(s, context->server_formats[i].nSamplesPerSec); /* nSamplesPerSec */ + + Stream_Write_UINT32(s, context->server_formats[i].nSamplesPerSec * + context->server_formats[i].nChannels * + context->server_formats[i].wBitsPerSample / 8); /* nAvgBytesPerSec */ + + Stream_Write_UINT16(s, context->server_formats[i].nBlockAlign); /* nBlockAlign */ + Stream_Write_UINT16(s, context->server_formats[i].wBitsPerSample); /* wBitsPerSample */ + Stream_Write_UINT16(s, context->server_formats[i].cbSize); /* cbSize */ + + if (context->server_formats[i].cbSize > 0) + { + Stream_Write(s, context->server_formats[i].data, context->server_formats[i].cbSize); + } + } + + pos = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, pos - 4); + Stream_SetPosition(s, pos); + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_GetPosition(s), NULL); + Stream_SetPosition(s, 0); + + return status; +} + +static void rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream* s) +{ + UINT16 timestamp = 0; + BYTE confirmBlockNum = 0; + Stream_Read_UINT16(s, timestamp); + Stream_Read_UINT8(s, confirmBlockNum); + Stream_Seek_UINT8(s); +} + +static void rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s) +{ + UINT16 quality; + + Stream_Read_UINT16(s, quality); + Stream_Seek_UINT16(s); // reserved + + fprintf(stderr, "Client requested sound quality: %#0X\n", quality); +} + +static BOOL rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) +{ + int i, num_known_format = 0; + UINT32 flags, vol, pitch; + UINT16 udpPort, version; + BYTE lastblock; + + Stream_Read_UINT32(s, flags); /* dwFlags */ + Stream_Read_UINT32(s, vol); /* dwVolume */ + Stream_Read_UINT32(s, pitch); /* dwPitch */ + Stream_Read_UINT16(s, udpPort); /* wDGramPort */ + Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ + Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ + Stream_Read_UINT16(s, version); /* wVersion */ + Stream_Seek_UINT8(s); /* bPad */ + + if (context->num_client_formats > 0) + { + context->client_formats = (AUDIO_FORMAT*) malloc(context->num_client_formats * sizeof(AUDIO_FORMAT)); + ZeroMemory(context->client_formats, sizeof(AUDIO_FORMAT)); + + for (i = 0; i < context->num_client_formats; i++) + { + Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); + Stream_Read_UINT16(s, context->client_formats[i].nChannels); + Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); + Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); + Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); + Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); + Stream_Read_UINT16(s, context->client_formats[i].cbSize); + + if (context->client_formats[i].cbSize > 0) + { + Stream_Seek(s, context->client_formats[i].cbSize); + } + + if (context->client_formats[i].wFormatTag != 0) + { + //lets call this a known format + //TODO: actually look through our own list of known formats + num_known_format++; + } + } + } + + if (num_known_format == 0) + { + fprintf(stderr, "Client doesn't support any known formats!\n"); + return FALSE; + } + + return TRUE; +} + +static void* rdpsnd_server_thread(void* arg) +{ + wStream* s; + DWORD status; + DWORD nCount; + void* buffer; + BYTE msgType; + UINT16 BodySize; + HANDLE events[8]; + HANDLE ChannelEvent; + UINT32 BytesReturned; + RdpsndServerContext* context; + + context = (RdpsndServerContext*) arg; + + buffer = NULL; + BytesReturned = 0; + ChannelEvent = NULL; + + s = Stream_New(NULL, 4096); + + if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + nCount = 0; + events[nCount++] = ChannelEvent; + events[nCount++] = context->priv->StopEvent; + + s = Stream_New(NULL, 4096); + rdpsnd_server_send_formats(context, s); + + while (1) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0) + { + break; + } + + Stream_SetPosition(s, 0); + + if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, + Stream_Buffer(s), Stream_Capacity(s), &BytesReturned)) + { + if (BytesReturned) + Stream_Seek(s, BytesReturned); + } + else + { + if (!BytesReturned) + break; + + Stream_EnsureRemainingCapacity(s, BytesReturned); + + if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0, + Stream_Buffer(s), Stream_Capacity(s), &BytesReturned) == FALSE) + { + break; + } + } + + Stream_Read_UINT8(s, msgType); + Stream_Seek_UINT8(s); /* bPad */ + Stream_Read_UINT16(s, BodySize); + + switch (msgType) + { + case SNDC_WAVECONFIRM: + rdpsnd_server_recv_waveconfirm(context, s); + break; + + case SNDC_QUALITYMODE: + rdpsnd_server_recv_quality_mode(context, s); + break; + + case SNDC_FORMATS: + if (rdpsnd_server_recv_formats(context, s)) + { + IFCALL(context->Activated, context); + } + break; + + default: + fprintf(stderr, "UNKOWN MESSAGE TYPE!! (%#0X)\n\n", msgType); + break; + } + } + + Stream_Free(s, TRUE); + + return NULL; +} + +static BOOL rdpsnd_server_initialize(RdpsndServerContext* context) +{ + context->Start(context); + return TRUE; +} + +static void rdpsnd_server_select_format(RdpsndServerContext* context, int client_format_index) +{ + int bs; + int out_buffer_size; + AUDIO_FORMAT *format; + + if (client_format_index < 0 || client_format_index >= context->num_client_formats) + { + fprintf(stderr, "rdpsnd_server_select_format: index %d is not correct.\n", client_format_index); + return; + } + + context->priv->src_bytes_per_sample = context->src_format.wBitsPerSample / 8; + context->priv->src_bytes_per_frame = context->priv->src_bytes_per_sample * context->src_format.nChannels; + + context->selected_client_format = client_format_index; + format = &context->client_formats[client_format_index]; + + if (format->nSamplesPerSec == 0) + { + fprintf(stderr, "Invalid Client Sound Format!!\n\n"); + return; + } + + if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM) + { + bs = (format->nBlockAlign - 4 * format->nChannels) * 4; + context->priv->out_frames = (format->nBlockAlign * 4 * format->nChannels * 2 / bs + 1) * bs / (format->nChannels * 2); + } + else if (format->wFormatTag == WAVE_FORMAT_ADPCM) + { + bs = (format->nBlockAlign - 7 * format->nChannels) * 2 / format->nChannels + 2; + context->priv->out_frames = bs * 4; + } + else + { + context->priv->out_frames = 0x4000 / context->priv->src_bytes_per_frame; + } + + if (format->nSamplesPerSec != context->src_format.nSamplesPerSec) + { + context->priv->out_frames = (context->priv->out_frames * context->src_format.nSamplesPerSec + format->nSamplesPerSec - 100) / format->nSamplesPerSec; + } + context->priv->out_pending_frames = 0; + + out_buffer_size = context->priv->out_frames * context->priv->src_bytes_per_frame; + + if (context->priv->out_buffer_size < out_buffer_size) + { + context->priv->out_buffer = (BYTE*) realloc(context->priv->out_buffer, out_buffer_size); + context->priv->out_buffer_size = out_buffer_size; + } + + freerdp_dsp_context_reset_adpcm(context->priv->dsp_context); +} + +static BOOL rdpsnd_server_send_audio_pdu(RdpsndServerContext* context) +{ + int size; + BYTE* src; + int frames; + int fill_size; + BOOL status; + AUDIO_FORMAT* format; + int tbytes_per_frame; + wStream* s = context->priv->rdpsnd_pdu; + + format = &context->client_formats[context->selected_client_format]; + tbytes_per_frame = format->nChannels * context->priv->src_bytes_per_sample; + + if ((format->nSamplesPerSec == context->src_format.nSamplesPerSec) && + (format->nChannels == context->src_format.nChannels)) + { + src = context->priv->out_buffer; + frames = context->priv->out_pending_frames; + } + else + { + context->priv->dsp_context->resample(context->priv->dsp_context, context->priv->out_buffer, + context->priv->src_bytes_per_sample, context->src_format.nChannels, + context->src_format.nSamplesPerSec, context->priv->out_pending_frames, + format->nChannels, format->nSamplesPerSec); + frames = context->priv->dsp_context->resampled_frames; + src = context->priv->dsp_context->resampled_buffer; + } + size = frames * tbytes_per_frame; + + if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM) + { + context->priv->dsp_context->encode_ima_adpcm(context->priv->dsp_context, + src, size, format->nChannels, format->nBlockAlign); + src = context->priv->dsp_context->adpcm_buffer; + size = context->priv->dsp_context->adpcm_size; + } + else if (format->wFormatTag == WAVE_FORMAT_ADPCM) + { + context->priv->dsp_context->encode_ms_adpcm(context->priv->dsp_context, + src, size, format->nChannels, format->nBlockAlign); + src = context->priv->dsp_context->adpcm_buffer; + size = context->priv->dsp_context->adpcm_size; + } + + context->block_no = (context->block_no + 1) % 256; + + /* Fill to nBlockAlign for the last audio packet */ + + fill_size = 0; + + if ((format->wFormatTag == WAVE_FORMAT_DVI_ADPCM || format->wFormatTag == WAVE_FORMAT_ADPCM) && + (context->priv->out_pending_frames < context->priv->out_frames) && ((size % format->nBlockAlign) != 0)) + { + fill_size = format->nBlockAlign - (size % format->nBlockAlign); + } + + /* WaveInfo PDU */ + Stream_SetPosition(s, 0); + Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT16(s, size + fill_size + 8); /* BodySize */ + + Stream_Write_UINT16(s, 0); /* wTimeStamp */ + Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */ + Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ + Stream_Seek(s, 3); /* bPad */ + Stream_Write(s, src, 4); + + WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_GetPosition(s), NULL); + Stream_SetPosition(s, 0); + + /* Wave PDU */ + Stream_EnsureRemainingCapacity(s, size + fill_size); + Stream_Write_UINT32(s, 0); /* bPad */ + Stream_Write(s, src + 4, size - 4); + + if (fill_size > 0) + Stream_Zero(s, fill_size); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_GetPosition(s), NULL); + Stream_SetPosition(s, 0); + + context->priv->out_pending_frames = 0; + + return status; +} + +static BOOL rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes) +{ + int cframes; + int cframesize; + + if (context->selected_client_format < 0) + return FALSE; + + while (nframes > 0) + { + cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); + cframesize = cframes * context->priv->src_bytes_per_frame; + + CopyMemory(context->priv->out_buffer + + (context->priv->out_pending_frames * context->priv->src_bytes_per_frame), buf, cframesize); + buf = (BYTE*) buf + cframesize; + nframes -= cframes; + context->priv->out_pending_frames += cframes; + + if (context->priv->out_pending_frames >= context->priv->out_frames) + { + if (!rdpsnd_server_send_audio_pdu(context)) + return FALSE; + } + } + + return TRUE; +} + +static BOOL rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right) +{ + int pos; + BOOL status; + wStream* s = context->priv->rdpsnd_pdu; + + Stream_Write_UINT8(s, SNDC_SETVOLUME); + Stream_Write_UINT8(s, 0); + Stream_Seek_UINT16(s); + + Stream_Write_UINT16(s, left); + Stream_Write_UINT16(s, right); + + pos = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, pos - 4); + Stream_SetPosition(s, pos); + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_GetPosition(s), NULL); + Stream_SetPosition(s, 0); + + return status; +} + +static BOOL rdpsnd_server_close(RdpsndServerContext* context) +{ + int pos; + BOOL status; + wStream* s = context->priv->rdpsnd_pdu; + + if (context->selected_client_format < 0) + return FALSE; + + if (context->priv->out_pending_frames > 0) + { + if (!rdpsnd_server_send_audio_pdu(context)) + return FALSE; + } + + context->selected_client_format = -1; + + Stream_Write_UINT8(s, SNDC_CLOSE); + Stream_Write_UINT8(s, 0); + Stream_Seek_UINT16(s); + + pos = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, pos - 4); + Stream_SetPosition(s, pos); + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, Stream_Buffer(s), Stream_GetPosition(s), NULL); + Stream_SetPosition(s, 0); + + return status; +} + +static int rdpsnd_server_start(RdpsndServerContext* context) +{ + context->priv->ChannelHandle = WTSVirtualChannelOpenEx(context->vcm, "rdpsnd", 0); + + if (!context->priv->ChannelHandle) + return -1; + + context->priv->rdpsnd_pdu = Stream_New(NULL, 4096); + + context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + context->priv->Thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) rdpsnd_server_thread, (void*) context, 0, NULL); + + return 0; +} + +static int rdpsnd_server_stop(RdpsndServerContext* context) +{ + SetEvent(context->priv->StopEvent); + + WaitForSingleObject(context->priv->Thread, INFINITE); + CloseHandle(context->priv->Thread); + + return 0; +} + +RdpsndServerContext* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm) +{ + RdpsndServerContext* context; + + context = (RdpsndServerContext*) malloc(sizeof(RdpsndServerContext)); + + if (context) + { + ZeroMemory(context, sizeof(RdpsndServerContext)); + + context->vcm = vcm; + + context->Start = rdpsnd_server_start; + context->Stop = rdpsnd_server_stop; + + context->selected_client_format = -1; + context->Initialize = rdpsnd_server_initialize; + context->SelectFormat = rdpsnd_server_select_format; + context->SendSamples = rdpsnd_server_send_samples; + context->SetVolume = rdpsnd_server_set_volume; + context->Close = rdpsnd_server_close; + + context->priv = (RdpsndServerPrivate*) malloc(sizeof(RdpsndServerPrivate)); + + if (context->priv) + { + ZeroMemory(context->priv, sizeof(RdpsndServerPrivate)); + + context->priv->dsp_context = freerdp_dsp_context_new(); + } + } + + return context; +} + +void rdpsnd_server_context_free(RdpsndServerContext* context) +{ + SetEvent(context->priv->StopEvent); + WaitForSingleObject(context->priv->Thread, INFINITE); + + if (context->priv->ChannelHandle) + WTSVirtualChannelClose(context->priv->ChannelHandle); + + if (context->priv->rdpsnd_pdu) + Stream_Free(context->priv->rdpsnd_pdu, TRUE); + + if (context->priv->out_buffer) + free(context->priv->out_buffer); + + if (context->priv->dsp_context) + freerdp_dsp_context_free(context->priv->dsp_context); + + if (context->client_formats) + free(context->client_formats); + + free(context); +} diff --git a/channels/rdpsnd/server/rdpsnd_main.h b/channels/rdpsnd/server/rdpsnd_main.h new file mode 100644 index 000000000..6322cee4a --- /dev/null +++ b/channels/rdpsnd/server/rdpsnd_main.h @@ -0,0 +1,48 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Server Audio Virtual Channel + * + * Copyright 2012 Vic Lee + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_SERVER_RDPSND_MAIN_H +#define FREERDP_CHANNEL_SERVER_RDPSND_MAIN_H + +#include +#include +#include + +#include +#include +#include + +struct _rdpsnd_server_private +{ + HANDLE Thread; + HANDLE StopEvent; + void* ChannelHandle; + + wStream* rdpsnd_pdu; + BYTE* out_buffer; + int out_buffer_size; + int out_frames; + int out_pending_frames; + UINT32 src_bytes_per_sample; + UINT32 src_bytes_per_frame; + FREERDP_DSP_CONTEXT* dsp_context; +}; + +#endif /* FREERDP_CHANNEL_SERVER_RDPSND_MAIN_H */ diff --git a/channels/server/channels.c b/channels/server/channels.c index 12935146a..a4c9bdc0b 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -44,6 +44,7 @@ #include #include #include +#include void freerdp_channels_dummy() { @@ -58,6 +59,9 @@ void freerdp_channels_dummy() rdpdr_server_context_new(NULL); rdpdr_server_context_free(NULL); + + drdynvc_server_context_new(NULL); + drdynvc_server_context_free(NULL); } /** diff --git a/include/freerdp/server/drdynvc.h b/include/freerdp/server/drdynvc.h new file mode 100644 index 000000000..31eb4ece6 --- /dev/null +++ b/include/freerdp/server/drdynvc.h @@ -0,0 +1,50 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Dynamic Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_SERVER_DRDYNVC_H +#define FREERDP_CHANNEL_SERVER_DRDYNVC_H + +#include +#include +#include + +/** + * Server Interface + */ + +typedef struct _drdynvc_client_context DrdynvcServerContext; +typedef struct _drdynvc_server_private DrdynvcServerPrivate; + +typedef int (*psDrdynvcStart)(DrdynvcServerContext* context); +typedef int (*psDrdynvcStop)(DrdynvcServerContext* context); + +struct _drdynvc_client_context +{ + WTSVirtualChannelManager* vcm; + + psDrdynvcStart Start; + psDrdynvcStop Stop; + + DrdynvcServerPrivate* priv; +}; + +FREERDP_API DrdynvcServerContext* drdynvc_server_context_new(WTSVirtualChannelManager* vcm); +FREERDP_API void drdynvc_server_context_free(DrdynvcServerContext* context); + +#endif /* FREERDP_CHANNEL_SERVER_DRDYNVC_H */ diff --git a/include/freerdp/server/rdpsnd.h b/include/freerdp/server/rdpsnd.h index 8b0c163c2..5b65cd8ff 100644 --- a/include/freerdp/server/rdpsnd.h +++ b/include/freerdp/server/rdpsnd.h @@ -23,20 +23,30 @@ #include #include +typedef struct _rdpsnd_server_context RdpsndServerContext; typedef struct _rdpsnd_server_context rdpsnd_server_context; +typedef struct _rdpsnd_server_private RdpsndServerPrivate; -typedef BOOL (*psRdpsndServerInitialize)(rdpsnd_server_context* context); -typedef void (*psRdpsndServerSelectFormat)(rdpsnd_server_context* context, int client_format_index); -typedef BOOL (*psRdpsndServerSendSamples)(rdpsnd_server_context* context, const void* buf, int nframes); -typedef BOOL (*psRdpsndServerSetVolume)(rdpsnd_server_context* context, int left, int right); -typedef BOOL (*psRdpsndServerClose)(rdpsnd_server_context* context); +typedef int (*psRdpsndStart)(RdpsndServerContext* context); +typedef int (*psRdpsndStop)(RdpsndServerContext* context); -typedef void (*psRdpsndServerActivated)(rdpsnd_server_context* context); +typedef BOOL (*psRdpsndServerInitialize)(RdpsndServerContext* context); +typedef void (*psRdpsndServerSelectFormat)(RdpsndServerContext* context, int client_format_index); +typedef BOOL (*psRdpsndServerSendSamples)(RdpsndServerContext* context, const void* buf, int nframes); +typedef BOOL (*psRdpsndServerSetVolume)(RdpsndServerContext* context, int left, int right); +typedef BOOL (*psRdpsndServerClose)(RdpsndServerContext* context); + +typedef void (*psRdpsndServerActivated)(RdpsndServerContext* context); struct _rdpsnd_server_context { WTSVirtualChannelManager* vcm; + psRdpsndStart Start; + psRdpsndStop Stop; + + RdpsndServerPrivate* priv; + /* Server self-defined pointer. */ void* data; @@ -95,8 +105,8 @@ struct _rdpsnd_server_context extern "C" { #endif -FREERDP_API rdpsnd_server_context* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm); -FREERDP_API void rdpsnd_server_context_free(rdpsnd_server_context* context); +FREERDP_API RdpsndServerContext* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm); +FREERDP_API void rdpsnd_server_context_free(RdpsndServerContext* context); #ifdef __cplusplus } diff --git a/server/Mac/mf_interface.h b/server/Mac/mf_interface.h index cf383ad89..a59951f4f 100644 --- a/server/Mac/mf_interface.h +++ b/server/Mac/mf_interface.h @@ -66,7 +66,7 @@ struct mf_peer_context //#endif //#ifdef CHANNEL_RDPSND_SERVER - rdpsnd_server_context* rdpsnd; + RdpsndServerContext* rdpsnd; //#endif }; diff --git a/server/Mac/mf_rdpsnd.c b/server/Mac/mf_rdpsnd.c index 2e273ded3..0f66f7e3c 100644 --- a/server/Mac/mf_rdpsnd.c +++ b/server/Mac/mf_rdpsnd.c @@ -34,7 +34,7 @@ static const AUDIO_FORMAT supported_audio_formats[] = { WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, NULL } }; -static void mf_peer_rdpsnd_activated(rdpsnd_server_context* context) +static void mf_peer_rdpsnd_activated(RdpsndServerContext* context) { OSStatus status; int i, j; diff --git a/server/Mac/mf_rdpsnd.h b/server/Mac/mf_rdpsnd.h index e635be7d7..2c3c6de2d 100644 --- a/server/Mac/mf_rdpsnd.h +++ b/server/Mac/mf_rdpsnd.h @@ -53,7 +53,7 @@ struct _AQRecorderState UInt32 bufferByteSize; SInt64 currentPacket; bool isRunning; - rdpsnd_server_context* snd_context; + RdpsndServerContext* snd_context; }; diff --git a/server/Sample/sf_rdpsnd.c b/server/Sample/sf_rdpsnd.c index 568db3bb5..9a1b70bcb 100644 --- a/server/Sample/sf_rdpsnd.c +++ b/server/Sample/sf_rdpsnd.c @@ -31,7 +31,7 @@ static const AUDIO_FORMAT test_audio_formats[] = { WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL } }; -static void sf_peer_rdpsnd_activated(rdpsnd_server_context* context) +static void sf_peer_rdpsnd_activated(RdpsndServerContext* context) { printf("RDPSND Activated\n"); } diff --git a/server/Sample/sfreerdp.h b/server/Sample/sfreerdp.h index b5a05a4cb..b54af158d 100644 --- a/server/Sample/sfreerdp.h +++ b/server/Sample/sfreerdp.h @@ -54,7 +54,7 @@ struct test_peer_context audin_server_context* audin; BOOL audin_open; UINT32 frame_id; - rdpsnd_server_context* rdpsnd; + RdpsndServerContext* rdpsnd; }; typedef struct test_peer_context testPeerContext; diff --git a/server/Windows/wf_directsound.c b/server/Windows/wf_directsound.c index cc0518073..f0ee7cb82 100644 --- a/server/Windows/wf_directsound.c +++ b/server/Windows/wf_directsound.c @@ -26,7 +26,7 @@ int wf_rdpsnd_set_latest_peer(wfPeerContext* peer) return 0; } -int wf_directsound_activate(rdpsnd_server_context* context) +int wf_directsound_activate(RdpsndServerContext* context) { HRESULT hr; wfInfo* wfi; diff --git a/server/Windows/wf_directsound.h b/server/Windows/wf_directsound.h index f88e7dfb0..9e51b27d2 100644 --- a/server/Windows/wf_directsound.h +++ b/server/Windows/wf_directsound.h @@ -6,7 +6,7 @@ int wf_rdpsnd_set_latest_peer(wfPeerContext* peer); -int wf_directsound_activate(rdpsnd_server_context* context); +int wf_directsound_activate(RdpsndServerContext* context); DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam); diff --git a/server/Windows/wf_interface.h b/server/Windows/wf_interface.h index 6352c5e2d..4108448b9 100644 --- a/server/Windows/wf_interface.h +++ b/server/Windows/wf_interface.h @@ -100,7 +100,7 @@ struct wf_peer_context HANDLE socketSemaphore; WTSVirtualChannelManager* vcm; - rdpsnd_server_context* rdpsnd; + RdpsndServerContext* rdpsnd; }; struct wf_server diff --git a/server/Windows/wf_rdpsnd.c b/server/Windows/wf_rdpsnd.c index 6b6847b7d..0a4362d43 100644 --- a/server/Windows/wf_rdpsnd.c +++ b/server/Windows/wf_rdpsnd.c @@ -46,7 +46,7 @@ static const AUDIO_FORMAT supported_audio_formats[] = { WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL } }; -static void wf_peer_rdpsnd_activated(rdpsnd_server_context* context) +static void wf_peer_rdpsnd_activated(RdpsndServerContext* context) { wfInfo* wfi; int i, j; diff --git a/server/Windows/wf_wasapi.c b/server/Windows/wf_wasapi.c index c24d495ea..7530ca76e 100644 --- a/server/Windows/wf_wasapi.c +++ b/server/Windows/wf_wasapi.c @@ -34,7 +34,7 @@ int wf_rdpsnd_set_latest_peer(wfPeerContext* peer) } -int wf_wasapi_activate(rdpsnd_server_context* context) +int wf_wasapi_activate(RdpsndServerContext* context) { wchar_t * pattern = L"Stereo Mix"; diff --git a/server/Windows/wf_wasapi.h b/server/Windows/wf_wasapi.h index 78cd0cbf6..7461f2a0d 100644 --- a/server/Windows/wf_wasapi.h +++ b/server/Windows/wf_wasapi.h @@ -6,7 +6,7 @@ int wf_rdpsnd_set_latest_peer(wfPeerContext* peer); -int wf_wasapi_activate(rdpsnd_server_context* context); +int wf_wasapi_activate(RdpsndServerContext* context); int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR * deviceStr);