channels: rdpsnd refactoring, drdynvc initial server-side code
This commit is contained in:
parent
3d0d65e917
commit
23a8354656
@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS)
|
|||||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_SERVER_CHANNELS)
|
||||||
|
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||||
|
endif()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
set(OPTION_DEFAULT OFF)
|
set(OPTION_DEFAULT OFF)
|
||||||
set(OPTION_CLIENT_DEFAULT ON)
|
set(OPTION_CLIENT_DEFAULT ON)
|
||||||
set(OPTION_SERVER_DEFAULT OFF)
|
set(OPTION_SERVER_DEFAULT ON)
|
||||||
|
|
||||||
define_channel_options(NAME "drdynvc" TYPE "static"
|
define_channel_options(NAME "drdynvc" TYPE "static"
|
||||||
DESCRIPTION "Dynamic Virtual Channel Extension"
|
DESCRIPTION "Dynamic Virtual Channel Extension"
|
||||||
|
35
channels/drdynvc/server/CMakeLists.txt
Normal file
35
channels/drdynvc/server/CMakeLists.txt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
|
# FreeRDP cmake build script
|
||||||
|
#
|
||||||
|
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
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")
|
149
channels/drdynvc/server/drdynvc_main.c
Normal file
149
channels/drdynvc/server/drdynvc_main.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
|
* Dynamic Virtual Channel Extension
|
||||||
|
*
|
||||||
|
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <winpr/crt.h>
|
||||||
|
#include <winpr/print.h>
|
||||||
|
#include <winpr/stream.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
37
channels/drdynvc/server/drdynvc_main.h
Normal file
37
channels/drdynvc/server/drdynvc_main.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
|
* Dynamic Virtual Channel Extension
|
||||||
|
*
|
||||||
|
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H
|
||||||
|
#define FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H
|
||||||
|
|
||||||
|
#include <winpr/crt.h>
|
||||||
|
#include <winpr/synch.h>
|
||||||
|
#include <winpr/thread.h>
|
||||||
|
|
||||||
|
#include <freerdp/settings.h>
|
||||||
|
#include <freerdp/server/drdynvc.h>
|
||||||
|
|
||||||
|
struct _drdynvc_server_private
|
||||||
|
{
|
||||||
|
HANDLE Thread;
|
||||||
|
HANDLE StopEvent;
|
||||||
|
void* ChannelHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FREERDP_CHANNEL_SERVER_DRDYNVC_MAIN_H */
|
@ -18,7 +18,8 @@
|
|||||||
define_channel_server("rdpsnd")
|
define_channel_server("rdpsnd")
|
||||||
|
|
||||||
set(${MODULE_PREFIX}_SRCS
|
set(${MODULE_PREFIX}_SRCS
|
||||||
rdpsnd.c)
|
rdpsnd_main.c
|
||||||
|
rdpsnd_main.h)
|
||||||
|
|
||||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||||
|
|
||||||
|
@ -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 <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <winpr/crt.h>
|
|
||||||
#include <winpr/print.h>
|
|
||||||
#include <winpr/synch.h>
|
|
||||||
#include <winpr/thread.h>
|
|
||||||
#include <winpr/stream.h>
|
|
||||||
|
|
||||||
#include <freerdp/codec/dsp.h>
|
|
||||||
#include <freerdp/channels/wtsvc.h>
|
|
||||||
#include <freerdp/server/rdpsnd.h>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
572
channels/rdpsnd/server/rdpsnd_main.c
Normal file
572
channels/rdpsnd/server/rdpsnd_main.c
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <winpr/crt.h>
|
||||||
|
#include <winpr/print.h>
|
||||||
|
#include <winpr/stream.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
48
channels/rdpsnd/server/rdpsnd_main.h
Normal file
48
channels/rdpsnd/server/rdpsnd_main.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
|
* Server Audio Virtual Channel
|
||||||
|
*
|
||||||
|
* Copyright 2012 Vic Lee
|
||||||
|
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FREERDP_CHANNEL_SERVER_RDPSND_MAIN_H
|
||||||
|
#define FREERDP_CHANNEL_SERVER_RDPSND_MAIN_H
|
||||||
|
|
||||||
|
#include <winpr/crt.h>
|
||||||
|
#include <winpr/synch.h>
|
||||||
|
#include <winpr/thread.h>
|
||||||
|
|
||||||
|
#include <freerdp/codec/dsp.h>
|
||||||
|
#include <freerdp/channels/wtsvc.h>
|
||||||
|
#include <freerdp/server/rdpsnd.h>
|
||||||
|
|
||||||
|
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 */
|
@ -44,6 +44,7 @@
|
|||||||
#include <freerdp/server/rdpsnd.h>
|
#include <freerdp/server/rdpsnd.h>
|
||||||
#include <freerdp/server/cliprdr.h>
|
#include <freerdp/server/cliprdr.h>
|
||||||
#include <freerdp/server/rdpdr.h>
|
#include <freerdp/server/rdpdr.h>
|
||||||
|
#include <freerdp/server/drdynvc.h>
|
||||||
|
|
||||||
void freerdp_channels_dummy()
|
void freerdp_channels_dummy()
|
||||||
{
|
{
|
||||||
@ -58,6 +59,9 @@ void freerdp_channels_dummy()
|
|||||||
|
|
||||||
rdpdr_server_context_new(NULL);
|
rdpdr_server_context_new(NULL);
|
||||||
rdpdr_server_context_free(NULL);
|
rdpdr_server_context_free(NULL);
|
||||||
|
|
||||||
|
drdynvc_server_context_new(NULL);
|
||||||
|
drdynvc_server_context_free(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
50
include/freerdp/server/drdynvc.h
Normal file
50
include/freerdp/server/drdynvc.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||||
|
* Dynamic Virtual Channel Extension
|
||||||
|
*
|
||||||
|
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FREERDP_CHANNEL_SERVER_DRDYNVC_H
|
||||||
|
#define FREERDP_CHANNEL_SERVER_DRDYNVC_H
|
||||||
|
|
||||||
|
#include <freerdp/api.h>
|
||||||
|
#include <freerdp/types.h>
|
||||||
|
#include <freerdp/channels/wtsvc.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 */
|
@ -23,20 +23,30 @@
|
|||||||
#include <freerdp/channels/wtsvc.h>
|
#include <freerdp/channels/wtsvc.h>
|
||||||
#include <freerdp/channels/rdpsnd.h>
|
#include <freerdp/channels/rdpsnd.h>
|
||||||
|
|
||||||
|
typedef struct _rdpsnd_server_context RdpsndServerContext;
|
||||||
typedef struct _rdpsnd_server_context rdpsnd_server_context;
|
typedef struct _rdpsnd_server_context rdpsnd_server_context;
|
||||||
|
typedef struct _rdpsnd_server_private RdpsndServerPrivate;
|
||||||
|
|
||||||
typedef BOOL (*psRdpsndServerInitialize)(rdpsnd_server_context* context);
|
typedef int (*psRdpsndStart)(RdpsndServerContext* context);
|
||||||
typedef void (*psRdpsndServerSelectFormat)(rdpsnd_server_context* context, int client_format_index);
|
typedef int (*psRdpsndStop)(RdpsndServerContext* context);
|
||||||
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 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
|
struct _rdpsnd_server_context
|
||||||
{
|
{
|
||||||
WTSVirtualChannelManager* vcm;
|
WTSVirtualChannelManager* vcm;
|
||||||
|
|
||||||
|
psRdpsndStart Start;
|
||||||
|
psRdpsndStop Stop;
|
||||||
|
|
||||||
|
RdpsndServerPrivate* priv;
|
||||||
|
|
||||||
/* Server self-defined pointer. */
|
/* Server self-defined pointer. */
|
||||||
void* data;
|
void* data;
|
||||||
|
|
||||||
@ -95,8 +105,8 @@ struct _rdpsnd_server_context
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FREERDP_API rdpsnd_server_context* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm);
|
FREERDP_API RdpsndServerContext* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm);
|
||||||
FREERDP_API void rdpsnd_server_context_free(rdpsnd_server_context* context);
|
FREERDP_API void rdpsnd_server_context_free(RdpsndServerContext* context);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ struct mf_peer_context
|
|||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
//#ifdef CHANNEL_RDPSND_SERVER
|
//#ifdef CHANNEL_RDPSND_SERVER
|
||||||
rdpsnd_server_context* rdpsnd;
|
RdpsndServerContext* rdpsnd;
|
||||||
//#endif
|
//#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ static const AUDIO_FORMAT supported_audio_formats[] =
|
|||||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, NULL }
|
{ 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;
|
OSStatus status;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -53,7 +53,7 @@ struct _AQRecorderState
|
|||||||
UInt32 bufferByteSize;
|
UInt32 bufferByteSize;
|
||||||
SInt64 currentPacket;
|
SInt64 currentPacket;
|
||||||
bool isRunning;
|
bool isRunning;
|
||||||
rdpsnd_server_context* snd_context;
|
RdpsndServerContext* snd_context;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ static const AUDIO_FORMAT test_audio_formats[] =
|
|||||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL }
|
{ 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");
|
printf("RDPSND Activated\n");
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ struct test_peer_context
|
|||||||
audin_server_context* audin;
|
audin_server_context* audin;
|
||||||
BOOL audin_open;
|
BOOL audin_open;
|
||||||
UINT32 frame_id;
|
UINT32 frame_id;
|
||||||
rdpsnd_server_context* rdpsnd;
|
RdpsndServerContext* rdpsnd;
|
||||||
};
|
};
|
||||||
typedef struct test_peer_context testPeerContext;
|
typedef struct test_peer_context testPeerContext;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wf_directsound_activate(rdpsnd_server_context* context)
|
int wf_directsound_activate(RdpsndServerContext* context)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
wfInfo* wfi;
|
wfInfo* wfi;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
|
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);
|
DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam);
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ struct wf_peer_context
|
|||||||
HANDLE socketSemaphore;
|
HANDLE socketSemaphore;
|
||||||
|
|
||||||
WTSVirtualChannelManager* vcm;
|
WTSVirtualChannelManager* vcm;
|
||||||
rdpsnd_server_context* rdpsnd;
|
RdpsndServerContext* rdpsnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wf_server
|
struct wf_server
|
||||||
|
@ -46,7 +46,7 @@ static const AUDIO_FORMAT supported_audio_formats[] =
|
|||||||
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL }
|
{ 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;
|
wfInfo* wfi;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -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";
|
wchar_t * pattern = L"Stereo Mix";
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
|
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);
|
int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR * deviceStr);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user