diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c index 3a080bd3d..f6ec5f9e6 100644 --- a/channels/audin/client/audin_main.c +++ b/channels/audin/client/audin_main.c @@ -44,7 +44,7 @@ #define SNDIN_VERSION 0x02 -enum MSG_SNDIN_CMD +typedef enum { MSG_SNDIN_VERSION = 0x01, MSG_SNDIN_FORMATS = 0x02, @@ -52,8 +52,8 @@ enum MSG_SNDIN_CMD MSG_SNDIN_OPEN_REPLY = 0x04, MSG_SNDIN_DATA_INCOMING = 0x05, MSG_SNDIN_DATA = 0x06, - MSG_SNDIN_FORMATCHANGE = 0x07 -}; + MSG_SNDIN_FORMATCHANGE = 0x07, +} MSG_SNDIN; typedef struct { diff --git a/channels/audin/server/audin.c b/channels/audin/server/audin.c index d24eaf10b..c76fcecd6 100644 --- a/channels/audin/server/audin.c +++ b/channels/audin/server/audin.c @@ -5,6 +5,7 @@ * Copyright 2012 Vic Lee * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2022 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +22,6 @@ #include -#include -#include -#include - #include #include #include @@ -32,337 +29,205 @@ #include #include -#include -#include -#include -#include #include #include #define TAG CHANNELS_TAG("audin.server") -#define MSG_SNDIN_VERSION 0x01 -#define MSG_SNDIN_FORMATS 0x02 -#define MSG_SNDIN_OPEN 0x03 -#define MSG_SNDIN_OPEN_REPLY 0x04 -#define MSG_SNDIN_DATA_INCOMING 0x05 -#define MSG_SNDIN_DATA 0x06 -#define MSG_SNDIN_FORMATCHANGE 0x07 + +#define SNDIN_HEADER_SIZE 1 + +typedef enum +{ + MSG_SNDIN_VERSION = 0x01, + MSG_SNDIN_FORMATS = 0x02, + MSG_SNDIN_OPEN = 0x03, + MSG_SNDIN_OPEN_REPLY = 0x04, + MSG_SNDIN_DATA_INCOMING = 0x05, + MSG_SNDIN_DATA = 0x06, + MSG_SNDIN_FORMATCHANGE = 0x07, +} MSG_SNDIN; typedef struct { audin_server_context context; - BOOL opened; - HANDLE stopEvent; HANDLE thread; void* audin_channel; DWORD SessionId; - - FREERDP_DSP_CONTEXT* dsp_context; - } audin_server; -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_select_format(audin_server_context* context, size_t client_format_index) +static UINT audin_server_recv_version(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) { - audin_server* audin = (audin_server*)context; + SNDIN_VERSION pdu = { 0 }; + UINT error = CHANNEL_RC_OK; - WINPR_ASSERT(audin); + WINPR_ASSERT(context); + WINPR_ASSERT(header); - if (client_format_index >= context->num_client_formats) - { - WLog_ERR(TAG, "error in protocol: client_format_index >= context->num_client_formats!"); - return ERROR_INVALID_DATA; - } + pdu.Header = *header; - context->selected_client_format = (SSIZE_T)client_format_index; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; - if (!freerdp_dsp_context_reset(audin->dsp_context, - &audin->context.client_formats[client_format_index], 0u)) - { - WLog_ERR(TAG, "Failed to reset dsp context format!"); - return ERROR_INTERNAL_ERROR; - } + Stream_Read_UINT32(s, pdu.Version); - if (audin->opened) - { - /* TODO: send MSG_SNDIN_FORMATCHANGE */ - } + IFCALLRET(context->ReceiveVersion, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->ReceiveVersion failed with error %" PRIu32 "", error); - return CHANNEL_RC_OK; + return error; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_send_version(audin_server* audin, wStream* s) +static UINT audin_server_recv_formats(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) { - ULONG written; - WINPR_ASSERT(audin); + SNDIN_FORMATS pdu = { 0 }; + UINT error = CHANNEL_RC_OK; - Stream_Write_UINT8(s, MSG_SNDIN_VERSION); - Stream_Write_UINT32(s, 1); /* Version (4 bytes) */ + WINPR_ASSERT(context); + WINPR_ASSERT(header); - WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX); - if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s), - (ULONG)Stream_GetPosition(s), &written)) + pdu.Header = *header; + + /* Implementations MUST, at a minimum, support WAVE_FORMAT_PCM (0x0001) */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4 + 4 + 18)) + return ERROR_NO_DATA; + + Stream_Read_UINT32(s, pdu.NumFormats); + Stream_Read_UINT32(s, pdu.cbSizeFormatsPacket); + + if (pdu.NumFormats == 0) { - WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); - return ERROR_INTERNAL_ERROR; - } - - return CHANNEL_RC_OK; -} - -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length) -{ - UINT32 Version; - - WINPR_ASSERT(audin); - if (length < 4) - { - WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %" PRIu32 "", - length); + WLog_ERR(TAG, "Sound Formats PDU contains no formats"); return ERROR_INVALID_DATA; } - Stream_Read_UINT32(s, Version); - - if (Version < 1) + pdu.SoundFormats = audio_formats_new(pdu.NumFormats); + if (!pdu.SoundFormats) { - WLog_ERR(TAG, "expected Version > 0 but got %" PRIu32 "", Version); - return ERROR_INVALID_DATA; - } - - return CHANNEL_RC_OK; -} - -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_send_formats(audin_server* audin, wStream* s) -{ - size_t i; - ULONG written; - WINPR_ASSERT(audin); - - Stream_SetPosition(s, 0); - Stream_Write_UINT8(s, MSG_SNDIN_FORMATS); - WINPR_ASSERT(audin->context.num_server_formats <= UINT32_MAX); - Stream_Write_UINT32(s, audin->context.num_server_formats); /* NumFormats (4 bytes) */ - Stream_Write_UINT32(s, 0); /* cbSizeFormatsPacket (4 bytes), client-to-server only */ - - for (i = 0; i < audin->context.num_server_formats; i++) - { - AUDIO_FORMAT format = audin->context.server_formats[i]; - - if (!audio_format_write(s, &format)) - { - WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); - return CHANNEL_RC_NO_MEMORY; - } - } - - WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX); - return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s), - (ULONG)Stream_GetPosition(s), &written) - ? CHANNEL_RC_OK - : ERROR_INTERNAL_ERROR; -} - -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length) -{ - size_t i; - UINT success = CHANNEL_RC_OK; - - WINPR_ASSERT(audin); - if (length < 8) - { - WLog_ERR(TAG, "error parsing rec formats: expected at least 8 bytes, got %" PRIu32 "", - length); - return ERROR_INVALID_DATA; - } - - Stream_Read_UINT32(s, audin->context.num_client_formats); /* NumFormats (4 bytes) */ - Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */ - length -= 8; - - if (audin->context.num_client_formats <= 0) - { - WLog_ERR(TAG, "num_client_formats expected > 0 but got %" PRIuz, - audin->context.num_client_formats); - return ERROR_INVALID_DATA; - } - - audin->context.client_formats = audio_formats_new(audin->context.num_client_formats); - - if (!audin->context.client_formats) + WLog_ERR(TAG, "Failed to allocate %u SoundFormats", pdu.NumFormats); return ERROR_NOT_ENOUGH_MEMORY; + } - for (i = 0; i < audin->context.num_client_formats; i++) + for (UINT32 i = 0; i < pdu.NumFormats; ++i) { - AUDIO_FORMAT* format = &audin->context.client_formats[i]; + AUDIO_FORMAT* format = &pdu.SoundFormats[i]; if (!audio_format_read(s, format)) { - audio_formats_free(audin->context.client_formats, i); - audin->context.client_formats = NULL; - WLog_ERR(TAG, "expected length at least 18, but got %" PRIu32 "", length); + WLog_ERR(TAG, "Failed to read audio format"); + audio_formats_free(pdu.SoundFormats, i + i); return ERROR_INVALID_DATA; } audio_format_print(WLog_Get(TAG), WLOG_DEBUG, format); } - IFCALLRET(audin->context.Opening, success, &audin->context); + if (pdu.cbSizeFormatsPacket != Stream_GetPosition(s)) + { + WLog_WARN(TAG, "cbSizeFormatsPacket is invalid! Expected: %u Got: %zu. Fixing size", + pdu.cbSizeFormatsPacket, Stream_GetPosition(s)); + pdu.cbSizeFormatsPacket = Stream_GetPosition(s); + } - if (success) - WLog_ERR(TAG, "context.Opening failed with error %" PRIu32 "", success); + pdu.ExtraDataSize = Stream_GetRemainingLength(s); - return success; + IFCALLRET(context->ReceiveFormats, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->ReceiveFormats failed with error %" PRIu32 "", error); + + audio_formats_free(pdu.SoundFormats, pdu.NumFormats); + + return error; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_send_open(audin_server* audin, wStream* s) +static UINT audin_server_recv_open_reply(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) { - ULONG written; + SNDIN_OPEN_REPLY pdu = { 0 }; + UINT error = CHANNEL_RC_OK; - WINPR_ASSERT(audin); - if (audin->context.selected_client_format < 0) - { - WLog_ERR(TAG, "audin->context.selected_client_format = %" PRIdz, - audin->context.selected_client_format); - return ERROR_INVALID_DATA; - } + WINPR_ASSERT(context); + WINPR_ASSERT(header); - audin->opened = TRUE; - Stream_SetPosition(s, 0); - Stream_Write_UINT8(s, MSG_SNDIN_OPEN); - Stream_Write_UINT32(s, audin->context.frames_per_packet); /* FramesPerPacket (4 bytes) */ - WINPR_ASSERT(audin->context.selected_client_format >= 0); - WINPR_ASSERT(audin->context.selected_client_format <= UINT32_MAX); - Stream_Write_UINT32( - s, (UINT32)audin->context.selected_client_format); /* initialFormat (4 bytes) */ - /* - * [MS-RDPEAI] 3.2.5.1.6 - * The second format specify the format that SHOULD be used to capture data from - * the actual audio input device. - */ - Stream_Write_UINT16(s, 1); /* wFormatTag = PCM */ - Stream_Write_UINT16(s, 2); /* nChannels */ - Stream_Write_UINT32(s, 44100); /* nSamplesPerSec */ - Stream_Write_UINT32(s, 44100 * 2 * 2); /* nAvgBytesPerSec */ - Stream_Write_UINT16(s, 4); /* nBlockAlign */ - Stream_Write_UINT16(s, 16); /* wBitsPerSample */ - Stream_Write_UINT16(s, 0); /* cbSize */ - WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX); - return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s), - (ULONG)Stream_GetPosition(s), &written) - ? CHANNEL_RC_OK - : ERROR_INTERNAL_ERROR; + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + Stream_Read_UINT32(s, pdu.Result); + + IFCALLRET(context->OpenReply, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->OpenReply failed with error %" PRIu32 "", error); + + return error; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length) +static UINT audin_server_recv_data_incoming(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) { - UINT32 Result; - UINT success = CHANNEL_RC_OK; + SNDIN_DATA_INCOMING pdu = { 0 }; + UINT error = CHANNEL_RC_OK; - WINPR_ASSERT(audin); - if (length < 4) - { - WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %" PRIu32 "", - length); - return ERROR_INVALID_DATA; - } + WINPR_ASSERT(context); + WINPR_ASSERT(header); - Stream_Read_UINT32(s, Result); - IFCALLRET(audin->context.OpenResult, success, &audin->context, Result); + pdu.Header = *header; - if (success) - WLog_ERR(TAG, "context.OpenResult failed with error %" PRIu32 "", success); + IFCALLRET(context->IncomingData, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->IncomingData failed with error %" PRIu32 "", error); - return success; + return error; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length) +static UINT audin_server_recv_data(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) { - AUDIO_FORMAT* format; - size_t sbytes_per_sample; - size_t sbytes_per_frame; - size_t frames; - wStream* out; - UINT success = ERROR_INTERNAL_ERROR; + SNDIN_DATA pdu = { 0 }; + wStream dataBuffer = { 0 }; + UINT error = CHANNEL_RC_OK; - WINPR_ASSERT(audin); - if (audin->context.selected_client_format < 0) - { - WLog_ERR(TAG, "audin->context.selected_client_format = %" PRIdz, - audin->context.selected_client_format); - return ERROR_INVALID_DATA; - } + WINPR_ASSERT(context); + WINPR_ASSERT(header); - out = Stream_New(NULL, 4096); + pdu.Header = *header; - if (!out) - return ERROR_OUTOFMEMORY; + pdu.Data = Stream_StaticInit(&dataBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s)); - format = &audin->context.client_formats[audin->context.selected_client_format]; + IFCALLRET(context->Data, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->Data failed with error %" PRIu32 "", error); - if (freerdp_dsp_decode(audin->dsp_context, format, Stream_Pointer(s), length, out)) - { - AUDIO_FORMAT dformat = *format; - dformat.wFormatTag = WAVE_FORMAT_PCM; - dformat.wBitsPerSample = 16; - Stream_SealLength(out); - Stream_SetPosition(out, 0); - sbytes_per_sample = format->wBitsPerSample / 8UL; - sbytes_per_frame = format->nChannels * sbytes_per_sample; - frames = Stream_Length(out) / sbytes_per_frame; - IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, &dformat, out, frames); + return error; +} - if (success) - WLog_ERR(TAG, "context.ReceiveSamples failed with error %" PRIu32 "", success); - } - else - WLog_ERR(TAG, "freerdp_dsp_decode failed!"); +static UINT audin_server_recv_format_change(audin_server_context* context, wStream* s, + const SNDIN_PDU* header) +{ + SNDIN_FORMATCHANGE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; - Stream_Free(out, TRUE); - return success; + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + Stream_Read_UINT32(s, pdu.NewFormat); + + IFCALLRET(context->ReceiveFormatChange, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->ReceiveFormatChange failed with error %" PRIu32 "", error); + + return error; } static DWORD WINAPI audin_server_thread_func(LPVOID arg) @@ -370,7 +235,6 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) wStream* s; void* buffer; DWORD nCount; - BYTE MessageId; HANDLE events[8]; BOOL ready = FALSE; HANDLE ChannelEvent; @@ -379,7 +243,6 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) UINT error = CHANNEL_RC_OK; DWORD status; buffer = NULL; - BytesReturned = 0; ChannelEvent = NULL; WINPR_ASSERT(audin); @@ -416,6 +279,8 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error); goto out; } + if (status == WAIT_OBJECT_0) + goto out; if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE) @@ -443,15 +308,21 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) if (ready) { - if ((error = audin_server_send_version(audin, s))) + SNDIN_VERSION version = { 0 }; + + version.Version = audin->context.serverVersion; + + if ((error = audin->context.SendVersion(&audin->context, &version))) { - WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!", error); + WLog_ERR(TAG, "SendVersion failed with error %" PRIu32 "!", error); goto out_capacity; } } while (ready) { + SNDIN_PDU header = { 0 }; + if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0) break; @@ -459,8 +330,10 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) { error = GetLastError(); WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error); - goto out; + break; } + if (status == WAIT_OBJECT_0) + break; Stream_SetPosition(s, 0); @@ -486,73 +359,43 @@ static DWORD WINAPI audin_server_thread_func(LPVOID arg) break; } - Stream_Read_UINT8(s, MessageId); - BytesReturned--; + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, SNDIN_HEADER_SIZE)) + { + error = ERROR_INTERNAL_ERROR; + break; + } - switch (MessageId) + Stream_Read_UINT8(s, header.MessageId); + + switch (header.MessageId) { case MSG_SNDIN_VERSION: - if ((error = audin_server_recv_version(audin, s, BytesReturned))) - { - WLog_ERR(TAG, "audin_server_recv_version failed with error %" PRIu32 "!", - error); - goto out_capacity; - } - - if ((error = audin_server_send_formats(audin, s))) - { - WLog_ERR(TAG, "audin_server_send_formats failed with error %" PRIu32 "!", - error); - goto out_capacity; - } - + error = audin_server_recv_version(&audin->context, s, &header); break; - case MSG_SNDIN_FORMATS: - if ((error = audin_server_recv_formats(audin, s, BytesReturned))) - { - WLog_ERR(TAG, "audin_server_recv_formats failed with error %" PRIu32 "!", - error); - goto out_capacity; - } - - if ((error = audin_server_send_open(audin, s))) - { - WLog_ERR(TAG, "audin_server_send_open failed with error %" PRIu32 "!", error); - goto out_capacity; - } - + error = audin_server_recv_formats(&audin->context, s, &header); break; - case MSG_SNDIN_OPEN_REPLY: - if ((error = audin_server_recv_open_reply(audin, s, BytesReturned))) - { - WLog_ERR(TAG, "audin_server_recv_open_reply failed with error %" PRIu32 "!", - error); - goto out_capacity; - } - + error = audin_server_recv_open_reply(&audin->context, s, &header); break; - case MSG_SNDIN_DATA_INCOMING: + error = audin_server_recv_data_incoming(&audin->context, s, &header); break; - case MSG_SNDIN_DATA: - if ((error = audin_server_recv_data(audin, s, BytesReturned))) - { - WLog_ERR(TAG, "audin_server_recv_data failed with error %" PRIu32 "!", error); - goto out_capacity; - } - + error = audin_server_recv_data(&audin->context, s, &header); break; - case MSG_SNDIN_FORMATCHANGE: + error = audin_server_recv_format_change(&audin->context, s, &header); break; - default: - WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId); + WLog_ERR(TAG, "audin_server_thread_func: unknown or invalid MessageId %" PRIu8 "", + header.MessageId); + error = ERROR_INVALID_DATA; break; } + if (error) + break; } out_capacity: @@ -664,10 +507,166 @@ static BOOL audin_server_close(audin_server_context* context) audin->audin_channel = NULL; } - audin->context.selected_client_format = -1; return TRUE; } +static wStream* audin_server_packet_new(size_t size, BYTE MessageId) +{ + wStream* s; + + /* Allocate what we need plus header bytes */ + s = Stream_New(NULL, size + SNDIN_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return NULL; + } + + Stream_Write_UINT8(s, MessageId); + + return s; +} + +static UINT audin_server_packet_send(audin_server_context* context, wStream* s) +{ + audin_server* audin = (audin_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written; + + WINPR_ASSERT(context); + WINPR_ASSERT(s); + + if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT audin_server_send_version(audin_server_context* context, const SNDIN_VERSION* version) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(version); + + s = audin_server_packet_new(4, MSG_SNDIN_VERSION); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, version->Version); + + return audin_server_packet_send(context, s); +} + +static UINT audin_server_send_formats(audin_server_context* context, const SNDIN_FORMATS* formats) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(formats); + + s = audin_server_packet_new(4 + 4 + 18, MSG_SNDIN_FORMATS); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, formats->NumFormats); + Stream_Write_UINT32(s, formats->cbSizeFormatsPacket); + + for (UINT32 i = 0; i < formats->NumFormats; ++i) + { + AUDIO_FORMAT* format = &formats->SoundFormats[i]; + + if (!audio_format_write(s, format)) + { + WLog_ERR(TAG, "Failed to write audio format"); + Stream_Free(s, TRUE); + return CHANNEL_RC_NO_MEMORY; + } + } + + return audin_server_packet_send(context, s); +} + +static UINT audin_server_send_open(audin_server_context* context, const SNDIN_OPEN* open) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(open); + + s = audin_server_packet_new(4 + 4 + 18 + 22, MSG_SNDIN_OPEN); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, open->FramesPerPacket); + Stream_Write_UINT32(s, open->initialFormat); + + Stream_Write_UINT16(s, open->captureFormat.wFormatTag); + Stream_Write_UINT16(s, open->captureFormat.nChannels); + Stream_Write_UINT32(s, open->captureFormat.nSamplesPerSec); + Stream_Write_UINT32(s, open->captureFormat.nAvgBytesPerSec); + Stream_Write_UINT16(s, open->captureFormat.nBlockAlign); + Stream_Write_UINT16(s, open->captureFormat.wBitsPerSample); + + if (open->ExtraFormatData) + { + Stream_Write_UINT16(s, 22); /* cbSize */ + + Stream_Write_UINT16(s, open->ExtraFormatData->Samples.wReserved); + Stream_Write_UINT32(s, open->ExtraFormatData->dwChannelMask); + + Stream_Write_UINT32(s, open->ExtraFormatData->SubFormat.Data1); + Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data2); + Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data3); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[0]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[1]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[2]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[3]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[4]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[5]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[6]); + Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[7]); + } + else + { + WINPR_ASSERT(open->captureFormat.wFormatTag != WAVE_FORMAT_EXTENSIBLE); + + Stream_Write_UINT16(s, 0); /* cbSize */ + } + + return audin_server_packet_send(context, s); +} + +static UINT audin_server_send_format_change(audin_server_context* context, + const SNDIN_FORMATCHANGE* format_change) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(format_change); + + s = audin_server_packet_new(4, MSG_SNDIN_FORMATCHANGE); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, format_change->NewFormat); + + return audin_server_packet_send(context, s); +} + audin_server_context* audin_server_context_new(HANDLE vcm) { audin_server* audin; @@ -680,20 +679,17 @@ audin_server_context* audin_server_context_new(HANDLE vcm) } audin->context.vcm = vcm; - audin->context.selected_client_format = -1; - audin->context.frames_per_packet = 4096; - audin->context.SelectFormat = audin_server_select_format; audin->context.Open = audin_server_open; audin->context.IsOpen = audin_server_is_open; audin->context.Close = audin_server_close; - audin->dsp_context = freerdp_dsp_context_new(FALSE); - if (!audin->dsp_context) - { - WLog_ERR(TAG, "freerdp_dsp_context_new failed!"); - free(audin); - return NULL; - } + audin->context.SendVersion = audin_server_send_version; + audin->context.SendFormats = audin_server_send_formats; + audin->context.SendOpen = audin_server_send_open; + audin->context.SendFormatChange = audin_server_send_format_change; + + /* Default values */ + audin->context.serverVersion = SNDIN_VERSION_Version_2; return (audin_server_context*)audin; } @@ -706,8 +702,5 @@ void audin_server_context_free(audin_server_context* context) return; audin_server_close(context); - freerdp_dsp_context_free(audin->dsp_context); - audio_formats_free(audin->context.client_formats, audin->context.num_client_formats); - audio_formats_free(audin->context.server_formats, audin->context.num_server_formats); free(audin); } diff --git a/include/freerdp/channels/audin.h b/include/freerdp/channels/audin.h index ebc27f1d1..b42aed1e5 100644 --- a/include/freerdp/channels/audin.h +++ b/include/freerdp/channels/audin.h @@ -3,6 +3,7 @@ * Audio Input Redirection Virtual Channel * * Copyright 2010-2011 Vic Lee + * Copyright 2023 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +22,102 @@ #define FREERDP_CHANNEL_AUDIN_H #include +#include #include #include #define AUDIN_DVC_CHANNEL_NAME "AUDIO_INPUT" +typedef struct +{ + BYTE MessageId; +} SNDIN_PDU; + +typedef enum +{ + SNDIN_VERSION_Version_1 = 0x00000001, + SNDIN_VERSION_Version_2 = 0x00000002, +} SNDIN_VERSION_Version; + +typedef struct +{ + SNDIN_PDU Header; + SNDIN_VERSION_Version Version; +} SNDIN_VERSION; + +typedef struct +{ + SNDIN_PDU Header; + UINT32 NumFormats; + UINT32 cbSizeFormatsPacket; + AUDIO_FORMAT* SoundFormats; + size_t ExtraDataSize; +} SNDIN_FORMATS; + +typedef enum +{ + SPEAKER_FRONT_LEFT = 0x00000001, + SPEAKER_FRONT_RIGHT = 0x00000002, + SPEAKER_FRONT_CENTER = 0x00000004, + SPEAKER_LOW_FREQUENCY = 0x00000008, + SPEAKER_BACK_LEFT = 0x00000010, + SPEAKER_BACK_RIGHT = 0x00000020, + SPEAKER_FRONT_LEFT_OF_CENTER = 0x00000040, + SPEAKER_FRONT_RIGHT_OF_CENTER = 0x00000080, + SPEAKER_BACK_CENTER = 0x00000100, + SPEAKER_SIDE_LEFT = 0x00000200, + SPEAKER_SIDE_RIGHT = 0x00000400, + SPEAKER_TOP_CENTER = 0x00000800, + SPEAKER_TOP_FRONT_LEFT = 0x00001000, + SPEAKER_TOP_FRONT_CENTER = 0x00002000, + SPEAKER_TOP_FRONT_RIGHT = 0x00004000, + SPEAKER_TOP_BACK_LEFT = 0x00008000, + SPEAKER_TOP_BACK_CENTER = 0x00010000, + SPEAKER_TOP_BACK_RIGHT = 0x00020000, +} AUDIN_SPEAKER; + +typedef struct +{ + union + { + UINT16 wValidBitsPerSample; + UINT16 wSamplesPerBlock; + UINT16 wReserved; + } Samples; + AUDIN_SPEAKER dwChannelMask; + GUID SubFormat; +} WAVEFORMAT_EXTENSIBLE; + +typedef struct +{ + SNDIN_PDU Header; + UINT32 FramesPerPacket; + UINT32 initialFormat; + AUDIO_FORMAT captureFormat; + WAVEFORMAT_EXTENSIBLE* ExtraFormatData; +} SNDIN_OPEN; + +typedef struct +{ + SNDIN_PDU Header; + UINT32 Result; +} SNDIN_OPEN_REPLY; + +typedef struct +{ + SNDIN_PDU Header; +} SNDIN_DATA_INCOMING; + +typedef struct +{ + SNDIN_PDU Header; + wStream* Data; +} SNDIN_DATA; + +typedef struct +{ + SNDIN_PDU Header; + UINT32 NewFormat; +} SNDIN_FORMATCHANGE; + #endif /* FREERDP_CHANNEL_AUDIN_H */ diff --git a/include/freerdp/codec/audio.h b/include/freerdp/codec/audio.h index cc91be950..80261bf3e 100644 --- a/include/freerdp/codec/audio.h +++ b/include/freerdp/codec/audio.h @@ -30,18 +30,18 @@ extern "C" { #endif -struct AUDIO_FORMAT -{ - UINT16 wFormatTag; - UINT16 nChannels; - UINT32 nSamplesPerSec; - UINT32 nAvgBytesPerSec; - UINT16 nBlockAlign; - UINT16 wBitsPerSample; - UINT16 cbSize; - BYTE* data; -}; -typedef struct AUDIO_FORMAT AUDIO_FORMAT; + struct AUDIO_FORMAT + { + UINT16 wFormatTag; + UINT16 nChannels; + UINT32 nSamplesPerSec; + UINT32 nAvgBytesPerSec; + UINT16 nBlockAlign; + UINT16 wBitsPerSample; + UINT16 cbSize; + BYTE* data; + }; + typedef struct AUDIO_FORMAT AUDIO_FORMAT; #define SNDC_CLOSE 1 #define SNDC_WAVE 2 @@ -65,10 +65,10 @@ typedef struct AUDIO_FORMAT AUDIO_FORMAT; #define MEDIUM_QUALITY 0x0001 #define HIGH_QUALITY 0x0002 -/* - * Format Tags: - * http://tools.ietf.org/html/rfc2361 - */ + /* + * Format Tags: + * http://tools.ietf.org/html/rfc2361 + */ #ifndef WAVE_FORMAT_UNKNOWN #define WAVE_FORMAT_UNKNOWN 0x0000 @@ -192,9 +192,11 @@ typedef struct AUDIO_FORMAT AUDIO_FORMAT; #endif /* !WAVE_FORMAT_LUCENT_G723 */ #define WAVE_FORMAT_AAC_MS 0xA106 -/** - * Audio Format Functions - */ +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE + + /** + * Audio Format Functions + */ FREERDP_API UINT32 audio_format_compute_time_length(const AUDIO_FORMAT* format, size_t size); diff --git a/include/freerdp/server/audin.h b/include/freerdp/server/audin.h index be272ce1b..b358eaabc 100644 --- a/include/freerdp/server/audin.h +++ b/include/freerdp/server/audin.h @@ -5,6 +5,7 @@ * Copyright 2012 Vic Lee * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2023 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +24,9 @@ #define FREERDP_CHANNEL_AUDIN_SERVER_H #include -#include + +#include #include -#include #if !defined(CHANNEL_AUDIN_SERVER) #error "This header must not be included if CHANNEL_AUDIN_SERVER is not defined" @@ -38,84 +39,122 @@ extern "C" typedef struct s_audin_server_context audin_server_context; + typedef BOOL (*psAudinServerChannelOpen)(audin_server_context* context); + typedef BOOL (*psAudinServerChannelIsOpen)(audin_server_context* context); + typedef BOOL (*psAudinServerChannelClose)(audin_server_context* context); + typedef BOOL (*psAudinServerChannelIdAssigned)(audin_server_context* context, UINT32 channelId); - typedef UINT (*psAudinServerSelectFormat)(audin_server_context* context, - size_t client_format_index); - typedef BOOL (*psAudinServerOpen)(audin_server_context* context); - typedef BOOL (*psAudinServerIsOpen)(audin_server_context* context); - typedef BOOL (*psAudinServerClose)(audin_server_context* context); - - typedef UINT (*psAudinServerOpening)(audin_server_context* context); - typedef UINT (*psAudinServerOpenResult)(audin_server_context* context, UINT32 result); - typedef UINT (*psAudinServerReceiveSamples)(audin_server_context* context, - const AUDIO_FORMAT* format, wStream* buf, - size_t nframes); + typedef UINT (*psAudinServerVersion)(audin_server_context* context, + const SNDIN_VERSION* version); + typedef UINT (*psAudinServerFormats)(audin_server_context* context, + const SNDIN_FORMATS* formats); + typedef UINT (*psAudinServerOpen)(audin_server_context* context, const SNDIN_OPEN* open); + typedef UINT (*psAudinServerOpenReply)(audin_server_context* context, + const SNDIN_OPEN_REPLY* open_reply); + typedef UINT (*psAudinServerIncomingData)(audin_server_context* context, + const SNDIN_DATA_INCOMING* data_incoming); + typedef UINT (*psAudinServerData)(audin_server_context* context, const SNDIN_DATA* data); + typedef UINT (*psAudinServerFormatChange)(audin_server_context* context, + const SNDIN_FORMATCHANGE* format_change); struct s_audin_server_context { HANDLE vcm; /* Server self-defined pointer. */ - void* data; + void* userdata; - /* Server supported formats. Set by server. */ - AUDIO_FORMAT* server_formats; - size_t num_server_formats; - - /* Server destination PCM audio format. Set by server. */ - AUDIO_FORMAT* dst_format; - - /* Server preferred frames per packet. */ - int frames_per_packet; - - /* Client supported formats. */ - AUDIO_FORMAT* client_formats; - size_t num_client_formats; - SSIZE_T selected_client_format; + /** + * Server version to send to the client, when the DVC was successfully + * opened. + **/ + SNDIN_VERSION_Version serverVersion; /*** APIs called by the server. ***/ - /** - * Choose the audio format to be received. The index argument is an index into - * the client_formats array and must be smaller than num_client_formats. - */ - psAudinServerSelectFormat SelectFormat; - /** - * Open the audio input stream. - */ - psAudinServerOpen Open; - - psAudinServerIsOpen IsOpen; /** - * Close the audio stream. + * Open the audio input channel. */ - psAudinServerClose Close; + psAudinServerChannelOpen Open; + + /** + * Check, whether the audio input channel thread was created + */ + psAudinServerChannelIsOpen IsOpen; + + /** + * Close the audio input channel. + */ + psAudinServerChannelClose Close; + + /** + * For the following server to client PDUs, + * the message header does not have to be set. + */ + + /** + * Send a Version PDU. + */ + psAudinServerVersion SendVersion; + + /** + * Send a Sound Formats PDU. + */ + psAudinServerFormats SendFormats; + + /** + * Send an Open PDU. + * + * In case of ExtraFormatData is not NULL, the SubFormat is always + * KSDATAFORMAT_SUBTYPE_PCM, i.e. it is not required to be explicitly set by + * the API user. + */ + psAudinServerOpen SendOpen; + + /** + * Send a Format Change PDU. + */ + psAudinServerFormatChange SendFormatChange; /*** Callbacks registered by the server. ***/ - /** - * It's ready to open the audio input stream. The server should examine client - * formats and call SelectFormat to choose the desired one in this callback. - */ - psAudinServerOpening Opening; - /** - * Client replied HRESULT of the open operation. - */ - psAudinServerOpenResult OpenResult; - /** - * Receive audio samples. Actual bytes in the buffer is: - * nframes * dst_format.nBitsPerSample * dst_format.nChannels / 8 - * Note that this callback is called from a different thread context so the - * server must be careful of thread synchronization. - */ - psAudinServerReceiveSamples ReceiveSamples; - - rdpContext* rdpcontext; /** * Callback, when the channel got its id assigned. */ psAudinServerChannelIdAssigned ChannelIdAssigned; + + /* + * Callback for the Version PDU. + */ + psAudinServerVersion ReceiveVersion; + + /* + * Callback for the Sound Formats PDU. + */ + psAudinServerFormats ReceiveFormats; + + /* + * Callback for the Open Reply PDU. + */ + psAudinServerOpenReply OpenReply; + + /* + * Callback for the Incoming Data PDU. + */ + psAudinServerIncomingData IncomingData; + + /* + * Callback for the Data PDU. + */ + psAudinServerData Data; + + /* + * Callback for the Format Change PDU. + */ + psAudinServerFormatChange ReceiveFormatChange; + + rdpContext* rdpcontext; }; FREERDP_API audin_server_context* audin_server_context_new(HANDLE vcm); diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 083652bc2..264aed487 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -99,7 +99,7 @@ extern "C" typedef BOOL (*pfnShadowChannelAudinServerReceiveSamples)(rdpShadowSubsystem* subsystem, rdpShadowClient* client, const AUDIO_FORMAT* format, - wStream* buf, size_t nframes); + wStream* data); struct rdp_shadow_client { @@ -128,173 +128,177 @@ extern "C" RemdeskServerContext* remdesk; RdpsndServerContext* rdpsnd; #if defined(CHANNEL_AUDIN_SERVER) - audin_server_context* audin; + audin_server_context* audin; + AUDIO_FORMAT* audin_server_formats; + size_t audin_n_server_formats; + AUDIO_FORMAT* audin_negotiated_format; + UINT32 audin_client_format_idx; #endif - RdpgfxServerContext* rdpgfx; + RdpgfxServerContext* rdpgfx; - BOOL resizeRequested; - UINT32 resizeWidth; - UINT32 resizeHeight; + BOOL resizeRequested; + UINT32 resizeWidth; + UINT32 resizeHeight; }; -struct rdp_shadow_server -{ - void* ext; - HANDLE thread; - HANDLE StopEvent; - wArrayList* clients; - rdpSettings* settings; - rdpShadowScreen* screen; - rdpShadowSurface* surface; - rdpShadowSurface* lobby; - rdpShadowCapture* capture; - rdpShadowSubsystem* subsystem; + struct rdp_shadow_server + { + void* ext; + HANDLE thread; + HANDLE StopEvent; + wArrayList* clients; + rdpSettings* settings; + rdpShadowScreen* screen; + rdpShadowSurface* surface; + rdpShadowSurface* lobby; + rdpShadowCapture* capture; + rdpShadowSubsystem* subsystem; - DWORD port; - BOOL mayView; - BOOL mayInteract; - BOOL shareSubRect; - BOOL authentication; - UINT32 selectedMonitor; - RECTANGLE_16 subRect; + DWORD port; + BOOL mayView; + BOOL mayInteract; + BOOL shareSubRect; + BOOL authentication; + UINT32 selectedMonitor; + RECTANGLE_16 subRect; - /* Codec settings */ - RLGR_MODE rfxMode; - H264_RATECONTROL_MODE h264RateControlMode; - UINT32 h264BitRate; - UINT32 h264FrameRate; - UINT32 h264QP; + /* Codec settings */ + RLGR_MODE rfxMode; + H264_RATECONTROL_MODE h264RateControlMode; + UINT32 h264BitRate; + UINT32 h264FrameRate; + UINT32 h264QP; - char* ipcSocket; - char* ConfigPath; - char* CertificateFile; - char* PrivateKeyFile; - CRITICAL_SECTION lock; - freerdp_listener* listener; -}; + char* ipcSocket; + char* ConfigPath; + char* CertificateFile; + char* PrivateKeyFile; + CRITICAL_SECTION lock; + freerdp_listener* listener; + }; -struct rdp_shadow_surface -{ - rdpShadowServer* server; + struct rdp_shadow_surface + { + rdpShadowServer* server; - UINT16 x; - UINT16 y; - UINT32 width; - UINT32 height; - UINT32 scanline; - DWORD format; - BYTE* data; + UINT16 x; + UINT16 y; + UINT32 width; + UINT32 height; + UINT32 scanline; + DWORD format; + BYTE* data; - CRITICAL_SECTION lock; - REGION16 invalidRegion; -}; + CRITICAL_SECTION lock; + REGION16 invalidRegion; + }; -struct S_RDP_SHADOW_ENTRY_POINTS -{ - pfnShadowSubsystemNew New; - pfnShadowSubsystemFree Free; + struct S_RDP_SHADOW_ENTRY_POINTS + { + pfnShadowSubsystemNew New; + pfnShadowSubsystemFree Free; - pfnShadowSubsystemInit Init; - pfnShadowSubsystemUninit Uninit; + pfnShadowSubsystemInit Init; + pfnShadowSubsystemUninit Uninit; - pfnShadowSubsystemStart Start; - pfnShadowSubsystemStop Stop; + pfnShadowSubsystemStart Start; + pfnShadowSubsystemStop Stop; - pfnShadowEnumMonitors EnumMonitors; -}; + pfnShadowEnumMonitors EnumMonitors; + }; -struct rdp_shadow_subsystem -{ - RDP_SHADOW_ENTRY_POINTS ep; - HANDLE event; - UINT32 numMonitors; - UINT32 captureFrameRate; - UINT32 selectedMonitor; - MONITOR_DEF monitors[16]; - MONITOR_DEF virtualScreen; + struct rdp_shadow_subsystem + { + RDP_SHADOW_ENTRY_POINTS ep; + HANDLE event; + UINT32 numMonitors; + UINT32 captureFrameRate; + UINT32 selectedMonitor; + MONITOR_DEF monitors[16]; + MONITOR_DEF virtualScreen; - /* This event indicates that we have graphic change */ - /* such as screen update and resize. It should not be */ - /* used by subsystem implementation directly */ - rdpShadowMultiClientEvent* updateEvent; + /* This event indicates that we have graphic change */ + /* such as screen update and resize. It should not be */ + /* used by subsystem implementation directly */ + rdpShadowMultiClientEvent* updateEvent; - wMessagePipe* MsgPipe; - UINT32 pointerX; - UINT32 pointerY; + wMessagePipe* MsgPipe; + UINT32 pointerX; + UINT32 pointerY; - AUDIO_FORMAT* rdpsndFormats; - size_t nRdpsndFormats; - AUDIO_FORMAT* audinFormats; - size_t nAudinFormats; + AUDIO_FORMAT* rdpsndFormats; + size_t nRdpsndFormats; + AUDIO_FORMAT* audinFormats; + size_t nAudinFormats; - pfnShadowSynchronizeEvent SynchronizeEvent; - pfnShadowKeyboardEvent KeyboardEvent; - pfnShadowUnicodeKeyboardEvent UnicodeKeyboardEvent; - pfnShadowMouseEvent MouseEvent; - pfnShadowExtendedMouseEvent ExtendedMouseEvent; - pfnShadowChannelAudinServerReceiveSamples AudinServerReceiveSamples; + pfnShadowSynchronizeEvent SynchronizeEvent; + pfnShadowKeyboardEvent KeyboardEvent; + pfnShadowUnicodeKeyboardEvent UnicodeKeyboardEvent; + pfnShadowMouseEvent MouseEvent; + pfnShadowExtendedMouseEvent ExtendedMouseEvent; + pfnShadowChannelAudinServerReceiveSamples AudinServerReceiveSamples; - pfnShadowAuthenticate Authenticate; - pfnShadowClientConnect ClientConnect; - pfnShadowClientDisconnect ClientDisconnect; - pfnShadowClientCapabilities ClientCapabilities; + pfnShadowAuthenticate Authenticate; + pfnShadowClientConnect ClientConnect; + pfnShadowClientDisconnect ClientDisconnect; + pfnShadowClientCapabilities ClientCapabilities; - rdpShadowServer* server; -}; + rdpShadowServer* server; + }; /* Definition of message between subsystem and clients */ #define SHADOW_MSG_IN_REFRESH_REQUEST_ID 1001 -typedef struct S_SHADOW_MSG_OUT SHADOW_MSG_OUT; -typedef void (*MSG_OUT_FREE_FN)(UINT32 id, - SHADOW_MSG_OUT* msg); /* function to free SHADOW_MSG_OUT */ + typedef struct S_SHADOW_MSG_OUT SHADOW_MSG_OUT; + typedef void (*MSG_OUT_FREE_FN)(UINT32 id, + SHADOW_MSG_OUT* msg); /* function to free SHADOW_MSG_OUT */ -struct S_SHADOW_MSG_OUT -{ - int refCount; - MSG_OUT_FREE_FN Free; -}; + struct S_SHADOW_MSG_OUT + { + int refCount; + MSG_OUT_FREE_FN Free; + }; #define SHADOW_MSG_OUT_POINTER_POSITION_UPDATE_ID 2001 #define SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE_ID 2002 #define SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID 2003 #define SHADOW_MSG_OUT_AUDIO_OUT_VOLUME_ID 2004 -typedef struct -{ - SHADOW_MSG_OUT common; - UINT32 xPos; - UINT32 yPos; -} SHADOW_MSG_OUT_POINTER_POSITION_UPDATE; + typedef struct + { + SHADOW_MSG_OUT common; + UINT32 xPos; + UINT32 yPos; + } SHADOW_MSG_OUT_POINTER_POSITION_UPDATE; -typedef struct -{ - SHADOW_MSG_OUT common; - UINT32 xHot; - UINT32 yHot; - UINT32 width; - UINT32 height; - UINT32 lengthAndMask; - UINT32 lengthXorMask; - BYTE* xorMaskData; - BYTE* andMaskData; -} SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE; + typedef struct + { + SHADOW_MSG_OUT common; + UINT32 xHot; + UINT32 yHot; + UINT32 width; + UINT32 height; + UINT32 lengthAndMask; + UINT32 lengthXorMask; + BYTE* xorMaskData; + BYTE* andMaskData; + } SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE; -typedef struct -{ - SHADOW_MSG_OUT common; - AUDIO_FORMAT* audio_format; - void* buf; - size_t nFrames; - UINT16 wTimestamp; -} SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES; + typedef struct + { + SHADOW_MSG_OUT common; + AUDIO_FORMAT* audio_format; + void* buf; + size_t nFrames; + UINT16 wTimestamp; + } SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES; -typedef struct -{ - SHADOW_MSG_OUT common; - UINT16 left; - UINT16 right; -} SHADOW_MSG_OUT_AUDIO_OUT_VOLUME; + typedef struct + { + SHADOW_MSG_OUT common; + UINT16 left; + UINT16 right; + } SHADOW_MSG_OUT_AUDIO_OUT_VOLUME; FREERDP_API void shadow_subsystem_set_entry_builtin(const char* name); FREERDP_API void shadow_subsystem_set_entry(pfnShadowSubsystemEntry pEntry); diff --git a/server/Mac/mf_audin.c b/server/Mac/mf_audin.c index 668a32435..b34e4c994 100644 --- a/server/Mac/mf_audin.c +++ b/server/Mac/mf_audin.c @@ -5,6 +5,7 @@ * Copyright 2012 Marc-Andre Moreau * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2023 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,47 +30,170 @@ #include #define TAG SERVER_TAG("mac") -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT mf_peer_audin_opening(audin_server_context* context) +static UINT mf_peer_audin_receive_version(audin_server_context* audin, const SNDIN_VERSION* version) { - context->SelectFormat(context, 0); + mfPeerContext* context; + SNDIN_FORMATS formats = { 0 }; + + WINPR_ASSERT(audin); + WINPR_ASSERT(version); + + context = audin->userdata; + WINPR_ASSERT(context); + + if (version->Version == 0) + { + WLog_ERR(TAG, "Received invalid AUDIO_INPUT version from client"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "AUDIO_INPUT version of client: %u", version->Version); + + formats.NumFormats = context->audin_n_server_formats; + formats.SoundFormats = context->audin_server_formats; + + return audin->SendFormats(audin, &formats); +} + +static UINT send_open(audin_server_context* audin) +{ + mfPeerContext* context; + SNDIN_OPEN open = { 0 }; + + WINPR_ASSERT(audin); + + context = audin->userdata; + WINPR_ASSERT(context); + + open.FramesPerPacket = 441; + open.initialFormat = context->audin_client_format_idx; + open.captureFormat.wFormatTag = WAVE_FORMAT_PCM; + open.captureFormat.nChannels = 2; + open.captureFormat.nSamplesPerSec = 44100; + open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2; + open.captureFormat.nBlockAlign = 4; + open.captureFormat.wBitsPerSample = 16; + + return audin->SendOpen(audin, &open); +} + +static UINT mf_peer_audin_receive_formats(audin_server_context* audin, const SNDIN_FORMATS* formats) +{ + mfPeerContext* context; + + WINPR_ASSERT(audin); + WINPR_ASSERT(formats); + + context = audin->userdata; + WINPR_ASSERT(context); + + if (context->audin_negotiated_format) + { + WLog_ERR(TAG, "Received client formats, but negotiation was already done"); + return ERROR_INVALID_DATA; + } + + for (size_t i = 0; i < context->audin_n_server_formats; ++i) + { + for (UINT32 j = 0; j < formats->NumFormats; ++j) + { + if (audio_format_compatible(&context->audin_server_formats[i], + &formats->SoundFormats[j])) + { + context->audin_negotiated_format = &context->audin_server_formats[i]; + context->audin_client_format_idx = i; + return send_open(audin); + } + } + } + + WLog_ERR(TAG, "Could not agree on a audio format with the server"); + + return ERROR_INVALID_DATA; +} + +static UINT mf_peer_audin_open_reply(audin_server_context* audin, + const SNDIN_OPEN_REPLY* open_reply) +{ + WINPR_ASSERT(audin); + WINPR_ASSERT(open_reply); + + /* TODO: Implement failure handling */ + WLog_DBG(TAG, "Open Reply PDU: Result: %i", open_reply->Result); return CHANNEL_RC_OK; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT mf_peer_audin_open_result(audin_server_context* context, UINT32 result) +static UINT mf_peer_audin_incoming_data(audin_server_context* audin, + const SNDIN_DATA_INCOMING* data_incoming) { + WINPR_ASSERT(audin); + WINPR_ASSERT(data_incoming); + + /* TODO: Implement bandwidth measure of clients uplink */ + WLog_DBG(TAG, "Received Incoming Data PDU"); return CHANNEL_RC_OK; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT mf_peer_audin_receive_samples(audin_server_context* context, const AUDIO_FORMAT* format, - wStream* buf, size_t nframes) +static UINT mf_peer_audin_data(audin_server_context* audin, const SNDIN_DATA* data) { + /* TODO: Implement */ + WINPR_ASSERT(audin); + WINPR_ASSERT(data); + + WLog_WARN(TAG, "not implemented"); + WLog_DBG(TAG, "receive %" PRIdz " bytes.", Stream_Length(data->Data)); + return CHANNEL_RC_OK; +} + +static UINT mf_peer_audin_receive_format_change(audin_server_context* audin, + const SNDIN_FORMATCHANGE* format_change) +{ + mfPeerContext* context; + + WINPR_ASSERT(audin); + WINPR_ASSERT(format_change); + + context = audin->userdata; + WINPR_ASSERT(context); + + if (format_change->NewFormat != context->audin_client_format_idx) + { + WLog_ERR(TAG, "NewFormat in FormatChange differs from requested format"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "Received Format Change PDU: %u", format_change->NewFormat); + return CHANNEL_RC_OK; } void mf_peer_audin_init(mfPeerContext* context) { + WINPR_ASSERT(context); + context->audin = audin_server_context_new(context->vcm); context->audin->rdpcontext = &context->_p; - context->audin->data = context; - context->audin->num_server_formats = server_audin_get_formats(&context->audin->server_formats); - if (context->audin->num_server_formats > 0) - context->audin->dst_format = &context->audin->server_formats[0]; - context->audin->Opening = mf_peer_audin_opening; - context->audin->OpenResult = mf_peer_audin_open_result; - context->audin->ReceiveSamples = mf_peer_audin_receive_samples; + context->audin->userdata = context; + + context->audin->ReceiveVersion = mf_peer_audin_receive_version; + context->audin->ReceiveFormats = mf_peer_audin_receive_formats; + context->audin->OpenReply = mf_peer_audin_open_reply; + context->audin->IncomingData = mf_peer_audin_incoming_data; + context->audin->Data = mf_peer_audin_data; + context->audin->ReceiveFormatChange = mf_peer_audin_receive_format_change; + + context->audin_n_server_formats = server_audin_get_formats(&context->audin_server_formats); +} + +void mf_peer_audin_uninit(mfPeerContext* context) +{ + WINPR_ASSERT(context); + + if (context->audin) + { + audio_formats_free(context->audin_server_formats, context->audin_n_server_formats); + context->audin_server_formats = NULL; + audin_server_context_free(context->audin); + context->audin = NULL; + } } diff --git a/server/Mac/mf_audin.h b/server/Mac/mf_audin.h index d0c2011c1..046144df3 100644 --- a/server/Mac/mf_audin.h +++ b/server/Mac/mf_audin.h @@ -29,4 +29,6 @@ void mf_peer_audin_init(mfPeerContext* context); +void mf_peer_audin_uninit(mfPeerContext* context); + #endif /* FREERDP_SERVER_MAC_AUDIN_H */ diff --git a/server/Mac/mf_interface.h b/server/Mac/mf_interface.h index c10b57f8d..bcd181263 100644 --- a/server/Mac/mf_interface.h +++ b/server/Mac/mf_interface.h @@ -30,19 +30,19 @@ #include -//#ifdef WITH_SERVER_CHANNELS +// #ifdef WITH_SERVER_CHANNELS #include -//#endif +// #endif -//#ifdef CHANNEL_RDPSND_SERVER +// #ifdef CHANNEL_RDPSND_SERVER #include -//#include "mf_rdpsnd.h" -//#endif +// #include "mf_rdpsnd.h" +// #endif -//#ifdef CHANNEL_AUDIN_SERVER +// #ifdef CHANNEL_AUDIN_SERVER #include -//#include "mf_audin.h" -//#endif +// #include "mf_audin.h" +// #endif typedef struct mf_info mfInfo; typedef struct mf_peer_context mfPeerContext; @@ -59,16 +59,20 @@ struct mf_peer_context RFX_CONTEXT* rfx_context; NSC_CONTEXT* nsc_context; - //#ifdef WITH_SERVER_CHANNELS + // #ifdef WITH_SERVER_CHANNELS HANDLE vcm; - //#endif - //#ifdef CHANNEL_AUDIN_SERVER + // #endif + // #ifdef CHANNEL_AUDIN_SERVER audin_server_context* audin; - //#endif + AUDIO_FORMAT* audin_server_formats; + size_t audin_n_server_formats; + AUDIO_FORMAT* audin_negotiated_format; + UINT32 audin_client_format_idx; + // #endif - //#ifdef CHANNEL_RDPSND_SERVER + // #ifdef CHANNEL_RDPSND_SERVER RdpsndServerContext* rdpsnd; - //#endif + // #endif }; struct mf_info diff --git a/server/Mac/mf_peer.c b/server/Mac/mf_peer.c index 86dd6d57b..92a7512ae 100644 --- a/server/Mac/mf_peer.c +++ b/server/Mac/mf_peer.c @@ -215,8 +215,7 @@ static void mf_peer_context_free(freerdp_peer* client, rdpContext* context) // nsc_context_free(peer->nsc_context); #ifdef CHANNEL_AUDIN_SERVER - if (peer->audin) - audin_server_context_free(peer->audin); + mf_peer_audin_uninit(peer); #endif #ifdef CHANNEL_RDPSND_SERVER diff --git a/server/Sample/sf_audin.c b/server/Sample/sf_audin.c index 85e221a98..52ca42b62 100644 --- a/server/Sample/sf_audin.c +++ b/server/Sample/sf_audin.c @@ -5,6 +5,7 @@ * Copyright 2012 Marc-Andre Moreau * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2023 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,51 +33,140 @@ #define TAG SERVER_TAG("sample") #if defined(CHANNEL_AUDIN_SERVER) -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT sf_peer_audin_opening(audin_server_context* context) +static UINT sf_peer_audin_receive_version(audin_server_context* audin, const SNDIN_VERSION* version) { + testPeerContext* context; + SNDIN_FORMATS formats = { 0 }; + + WINPR_ASSERT(audin); + WINPR_ASSERT(version); + + context = audin->userdata; WINPR_ASSERT(context); - WLog_DBG(TAG, "AUDIN opening."); - /* Simply choose the first format supported by the client. */ - context->SelectFormat(context, 0); + if (version->Version == 0) + { + WLog_ERR(TAG, "Received invalid AUDIO_INPUT version from client"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "AUDIO_INPUT version of client: %u", version->Version); + + formats.NumFormats = context->audin_n_server_formats; + formats.SoundFormats = context->audin_server_formats; + + return audin->SendFormats(audin, &formats); +} + +static UINT send_open(audin_server_context* audin) +{ + testPeerContext* context; + SNDIN_OPEN open = { 0 }; + + WINPR_ASSERT(audin); + + context = audin->userdata; + WINPR_ASSERT(context); + + open.FramesPerPacket = 441; + open.initialFormat = context->audin_client_format_idx; + open.captureFormat.wFormatTag = WAVE_FORMAT_PCM; + open.captureFormat.nChannels = 2; + open.captureFormat.nSamplesPerSec = 44100; + open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2; + open.captureFormat.nBlockAlign = 4; + open.captureFormat.wBitsPerSample = 16; + + return audin->SendOpen(audin, &open); +} + +static UINT sf_peer_audin_receive_formats(audin_server_context* audin, const SNDIN_FORMATS* formats) +{ + testPeerContext* context; + + WINPR_ASSERT(audin); + WINPR_ASSERT(formats); + + context = audin->userdata; + WINPR_ASSERT(context); + + if (context->audin_negotiated_format) + { + WLog_ERR(TAG, "Received client formats, but negotiation was already done"); + return ERROR_INVALID_DATA; + } + + for (size_t i = 0; i < context->audin_n_server_formats; ++i) + { + for (UINT32 j = 0; j < formats->NumFormats; ++j) + { + if (audio_format_compatible(&context->audin_server_formats[i], + &formats->SoundFormats[j])) + { + context->audin_negotiated_format = &context->audin_server_formats[i]; + context->audin_client_format_idx = i; + return send_open(audin); + } + } + } + + WLog_ERR(TAG, "Could not agree on a audio format with the server"); + + return ERROR_INVALID_DATA; +} + +static UINT sf_peer_audin_open_reply(audin_server_context* audin, + const SNDIN_OPEN_REPLY* open_reply) +{ + WINPR_ASSERT(audin); + WINPR_ASSERT(open_reply); + + /* TODO: Implement failure handling */ + WLog_DBG(TAG, "Open Reply PDU: Result: %i", open_reply->Result); return CHANNEL_RC_OK; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT sf_peer_audin_open_result(audin_server_context* context, UINT32 result) +static UINT sf_peer_audin_incoming_data(audin_server_context* audin, + const SNDIN_DATA_INCOMING* data_incoming) { - /* TODO: Implement */ - WINPR_ASSERT(context); + WINPR_ASSERT(audin); + WINPR_ASSERT(data_incoming); - WLog_WARN(TAG, "not implemented"); - WLog_DBG(TAG, "AUDIN open result %" PRIu32 ".", result); + /* TODO: Implement bandwidth measure of clients uplink */ + WLog_DBG(TAG, "Received Incoming Data PDU"); return CHANNEL_RC_OK; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT sf_peer_audin_receive_samples(audin_server_context* context, const AUDIO_FORMAT* format, - wStream* buf, size_t nframes) +static UINT sf_peer_audin_data(audin_server_context* audin, const SNDIN_DATA* data) { /* TODO: Implement */ - WINPR_ASSERT(context); - WINPR_ASSERT(format); - WINPR_ASSERT(buf); + WINPR_ASSERT(audin); + WINPR_ASSERT(data); WLog_WARN(TAG, "not implemented"); - WLog_DBG(TAG, "receive %" PRIdz " frames.", nframes); + WLog_DBG(TAG, "receive %" PRIdz " bytes.", Stream_Length(data->Data)); + return CHANNEL_RC_OK; +} + +static UINT sf_peer_audin_receive_format_change(audin_server_context* audin, + const SNDIN_FORMATCHANGE* format_change) +{ + testPeerContext* context; + + WINPR_ASSERT(audin); + WINPR_ASSERT(format_change); + + context = audin->userdata; + WINPR_ASSERT(context); + + if (format_change->NewFormat != context->audin_client_format_idx) + { + WLog_ERR(TAG, "NewFormat in FormatChange differs from requested format"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "Received Format Change PDU: %u", format_change->NewFormat); + return CHANNEL_RC_OK; } #endif @@ -89,15 +179,16 @@ void sf_peer_audin_init(testPeerContext* context) WINPR_ASSERT(context->audin); context->audin->rdpcontext = &context->_p; - context->audin->data = context; - context->audin->num_server_formats = server_audin_get_formats(&context->audin->server_formats); + context->audin->userdata = context; - if (context->audin->num_server_formats > 0) - context->audin->dst_format = &context->audin->server_formats[0]; + context->audin->ReceiveVersion = sf_peer_audin_receive_version; + context->audin->ReceiveFormats = sf_peer_audin_receive_formats; + context->audin->OpenReply = sf_peer_audin_open_reply; + context->audin->IncomingData = sf_peer_audin_incoming_data; + context->audin->Data = sf_peer_audin_data; + context->audin->ReceiveFormatChange = sf_peer_audin_receive_format_change; - context->audin->Opening = sf_peer_audin_opening; - context->audin->OpenResult = sf_peer_audin_open_result; - context->audin->ReceiveSamples = sf_peer_audin_receive_samples; + context->audin_n_server_formats = server_audin_get_formats(&context->audin_server_formats); #endif } @@ -139,7 +230,15 @@ BOOL sf_peer_audin_running(testPeerContext* context) void sf_peer_audin_uninit(testPeerContext* context) { + WINPR_ASSERT(context); + #if defined(CHANNEL_AUDIN_SERVER) - audin_server_context_free(context->audin); + if (context->audin) + { + audio_formats_free(context->audin_server_formats, context->audin_n_server_formats); + context->audin_server_formats = NULL; + audin_server_context_free(context->audin); + context->audin = NULL; + } #endif } diff --git a/server/Sample/sfreerdp.h b/server/Sample/sfreerdp.h index 4baf09f6d..ed741637e 100644 --- a/server/Sample/sfreerdp.h +++ b/server/Sample/sfreerdp.h @@ -60,6 +60,10 @@ struct test_peer_context HANDLE debug_channel_thread; #if defined(CHANNEL_AUDIN_SERVER) audin_server_context* audin; + AUDIO_FORMAT* audin_server_formats; + size_t audin_n_server_formats; + AUDIO_FORMAT* audin_negotiated_format; + UINT32 audin_client_format_idx; #endif BOOL audin_open; #if defined(CHANNEL_AINPUT_SERVER) diff --git a/server/shadow/shadow_audin.c b/server/shadow/shadow_audin.c index e78a25492..d920d71a2 100644 --- a/server/shadow/shadow_audin.c +++ b/server/shadow/shadow_audin.c @@ -2,6 +2,7 @@ * FreeRDP: A Remote Desktop Protocol Implementation * * Copyright 2015 Jiang Zihao + * Copyright 2023 Pascal Nowack * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +20,6 @@ #include #include -#include #include "shadow.h" #include "shadow_audin.h" @@ -32,75 +32,161 @@ #define TAG SERVER_TAG("shadow") #if defined(CHANNEL_AUDIN_SERVER) -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT AudinServerOpening(audin_server_context* context) -{ - AUDIO_FORMAT* agreed_format = NULL; - size_t i = 0, j = 0; - for (i = 0; i < context->num_client_formats; i++) +static UINT AudinServerReceiveVersion(audin_server_context* audin, const SNDIN_VERSION* version) +{ + rdpShadowClient* client; + SNDIN_FORMATS formats = { 0 }; + + WINPR_ASSERT(audin); + WINPR_ASSERT(version); + + client = audin->userdata; + WINPR_ASSERT(client); + + if (version->Version == 0) { - for (j = 0; j < context->num_server_formats; j++) + WLog_ERR(TAG, "Received invalid AUDIO_INPUT version from client"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "AUDIO_INPUT version of client: %u", version->Version); + + formats.NumFormats = client->audin_n_server_formats; + formats.SoundFormats = client->audin_server_formats; + + return audin->SendFormats(audin, &formats); +} + +static UINT send_open(audin_server_context* audin) +{ + rdpShadowClient* client = audin->userdata; + SNDIN_OPEN open = { 0 }; + + WINPR_ASSERT(audin); + + client = audin->userdata; + WINPR_ASSERT(client); + + open.FramesPerPacket = 441; + open.initialFormat = client->audin_client_format_idx; + open.captureFormat.wFormatTag = WAVE_FORMAT_PCM; + open.captureFormat.nChannels = 2; + open.captureFormat.nSamplesPerSec = 44100; + open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2; + open.captureFormat.nBlockAlign = 4; + open.captureFormat.wBitsPerSample = 16; + + return audin->SendOpen(audin, &open); +} + +static UINT AudinServerReceiveFormats(audin_server_context* audin, const SNDIN_FORMATS* formats) +{ + rdpShadowClient* client; + + WINPR_ASSERT(audin); + WINPR_ASSERT(formats); + + client = audin->userdata; + WINPR_ASSERT(client); + + if (client->audin_negotiated_format) + { + WLog_ERR(TAG, "Received client formats, but negotiation was already done"); + return ERROR_INVALID_DATA; + } + + for (size_t i = 0; i < client->audin_n_server_formats; ++i) + { + for (UINT32 j = 0; j < formats->NumFormats; ++j) { - if (audio_format_compatible(&context->server_formats[j], &context->client_formats[i])) + if (audio_format_compatible(&client->audin_server_formats[i], + &formats->SoundFormats[j])) { - agreed_format = &context->server_formats[j]; - break; + client->audin_negotiated_format = &client->audin_server_formats[i]; + client->audin_client_format_idx = i; + return send_open(audin); } } - - if (agreed_format != NULL) - break; } - if (agreed_format == NULL) - { - WLog_ERR(TAG, "Could not agree on a audio format with the server\n"); - return CHANNEL_RC_OK; - } + WLog_ERR(TAG, "Could not agree on a audio format with the server"); - return IFCALLRESULT(ERROR_CALL_NOT_IMPLEMENTED, context->SelectFormat, context, i); + return ERROR_INVALID_DATA; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT AudinServerOpenResult(audin_server_context* context, UINT32 result) + +static UINT AudinServerOpenReply(audin_server_context* audin, const SNDIN_OPEN_REPLY* open_reply) { - /* TODO: Implement */ - WLog_WARN(TAG, "not implemented"); - WLog_INFO(TAG, "AUDIN open result %" PRIu32 ".\n", result); + WINPR_ASSERT(audin); + WINPR_ASSERT(open_reply); + + /* TODO: Implement failure handling */ + WLog_DBG(TAG, "Open Reply PDU: Result: %i", open_reply->Result); return CHANNEL_RC_OK; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ -static UINT AudinServerReceiveSamples(audin_server_context* context, const AUDIO_FORMAT* format, - wStream* buf, size_t nframes) + +static UINT AudinServerIncomingData(audin_server_context* audin, + const SNDIN_DATA_INCOMING* data_incoming) { - rdpShadowClient* client = (rdpShadowClient*)context->data; - rdpShadowSubsystem* subsystem = client->server->subsystem; + WINPR_ASSERT(audin); + WINPR_ASSERT(data_incoming); + + /* TODO: Implement bandwidth measure of clients uplink */ + WLog_DBG(TAG, "Received Incoming Data PDU"); + return CHANNEL_RC_OK; +} + +static UINT AudinServerData(audin_server_context* audin, const SNDIN_DATA* data) +{ + rdpShadowClient* client; + rdpShadowSubsystem* subsystem; + + WINPR_ASSERT(audin); + WINPR_ASSERT(data); + + client = audin->userdata; + WINPR_ASSERT(client); + WINPR_ASSERT(client->server); + subsystem = client->server->subsystem; + WINPR_ASSERT(subsystem); if (!client->mayInteract) return CHANNEL_RC_OK; - if (!IFCALLRESULT(TRUE, subsystem->AudinServerReceiveSamples, subsystem, client, format, buf, - nframes)) + if (!IFCALLRESULT(TRUE, subsystem->AudinServerReceiveSamples, subsystem, client, + client->audin_negotiated_format, data->Data)) return ERROR_INTERNAL_ERROR; return CHANNEL_RC_OK; } + +static UINT AudinServerReceiveFormatChange(audin_server_context* audin, + const SNDIN_FORMATCHANGE* format_change) +{ + rdpShadowClient* client; + + WINPR_ASSERT(audin); + WINPR_ASSERT(format_change); + + client = audin->userdata; + WINPR_ASSERT(client); + + if (format_change->NewFormat != client->audin_client_format_idx) + { + WLog_ERR(TAG, "NewFormat in FormatChange differs from requested format"); + return ERROR_INVALID_DATA; + } + + WLog_DBG(TAG, "Received Format Change PDU: %u", format_change->NewFormat); + + return CHANNEL_RC_OK; +} #endif BOOL shadow_client_audin_init(rdpShadowClient* client) { + WINPR_ASSERT(client); + #if defined(CHANNEL_AUDIN_SERVER) audin_server_context* audin; audin = client->audin = audin_server_context_new(client->vcm); @@ -108,36 +194,42 @@ BOOL shadow_client_audin_init(rdpShadowClient* client) if (!audin) return FALSE; - audin->data = client; + audin->userdata = client; + + audin->ReceiveVersion = AudinServerReceiveVersion; + audin->ReceiveFormats = AudinServerReceiveFormats; + audin->OpenReply = AudinServerOpenReply; + audin->IncomingData = AudinServerIncomingData; + audin->Data = AudinServerData; + audin->ReceiveFormatChange = AudinServerReceiveFormatChange; if (client->subsystem->audinFormats) { size_t x; - audin->server_formats = audio_formats_new(client->subsystem->nAudinFormats); + client->audin_server_formats = audio_formats_new(client->subsystem->nAudinFormats); - if (!audin->server_formats) + if (!client->audin_server_formats) goto fail; for (x = 0; x < client->subsystem->nAudinFormats; x++) { - if (!audio_format_copy(&client->subsystem->audinFormats[x], &audin->server_formats[x])) + if (!audio_format_copy(&client->subsystem->audinFormats[x], + &client->audin_server_formats[x])) goto fail; } - audin->num_server_formats = client->subsystem->nAudinFormats; + client->audin_n_server_formats = client->subsystem->nAudinFormats; } else { - audin->num_server_formats = server_audin_get_formats(&audin->server_formats); + client->audin_n_server_formats = server_audin_get_formats(&client->audin_server_formats); } - if (audin->num_server_formats < 1) + if (client->audin_n_server_formats < 1) goto fail; - audin->dst_format = &audin->server_formats[0]; - audin->Opening = AudinServerOpening; - audin->OpenResult = AudinServerOpenResult; - audin->ReceiveSamples = AudinServerReceiveSamples; + client->audin_negotiated_format = NULL; + return TRUE; fail: audin_server_context_free(audin); @@ -148,9 +240,13 @@ fail: void shadow_client_audin_uninit(rdpShadowClient* client) { + WINPR_ASSERT(client); + #if defined(CHANNEL_AUDIN_SERVER) if (client->audin) { + audio_formats_free(client->audin_server_formats, client->audin_n_server_formats); + client->audin_server_formats = NULL; audin_server_context_free(client->audin); client->audin = NULL; }