23#include <freerdp/config.h>
26#include <winpr/assert.h>
27#include <winpr/synch.h>
28#include <winpr/thread.h>
29#include <winpr/stream.h>
31#include <freerdp/freerdp.h>
32#include <freerdp/server/server-common.h>
33#include <freerdp/server/audin.h>
34#include <freerdp/channels/log.h>
36#define AUDIN_TAG CHANNELS_TAG("audin.server")
38#define SNDIN_HEADER_SIZE 1
42 MSG_SNDIN_VERSION = 0x01,
43 MSG_SNDIN_FORMATS = 0x02,
44 MSG_SNDIN_OPEN = 0x03,
45 MSG_SNDIN_OPEN_REPLY = 0x04,
46 MSG_SNDIN_DATA_INCOMING = 0x05,
47 MSG_SNDIN_DATA = 0x06,
48 MSG_SNDIN_FORMATCHANGE = 0x07,
53 audin_server_context context;
63 UINT32 audin_n_server_formats;
65 UINT32 audin_client_format_idx;
69static UINT audin_server_recv_version(audin_server_context* context,
wStream* s,
72 audin_server* audin = (audin_server*)context;
74 UINT error = CHANNEL_RC_OK;
76 WINPR_ASSERT(context);
81 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
85 const UINT32 version = Stream_Get_UINT32(s);
88 case SNDIN_VERSION_Version_1:
89 pdu.Version = SNDIN_VERSION_Version_1;
91 case SNDIN_VERSION_Version_2:
92 pdu.Version = SNDIN_VERSION_Version_2;
95 pdu.Version = SNDIN_VERSION_Version_2;
96 WLog_Print(audin->log, WLOG_WARN,
97 "Received unsupported channel version %" PRIu32
98 ", using highest supported version %u",
99 version, pdu.Version);
104 IFCALLRET(context->ReceiveVersion, error, context, &pdu);
106 WLog_Print(audin->log, WLOG_ERROR,
"context->ReceiveVersion failed with error %" PRIu32
"",
112static UINT audin_server_recv_formats(audin_server_context* context,
wStream* s,
115 audin_server* audin = (audin_server*)context;
117 UINT error = CHANNEL_RC_OK;
119 WINPR_ASSERT(context);
120 WINPR_ASSERT(header);
122 pdu.Header = *header;
125 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4 + 4 + 18))
126 return ERROR_NO_DATA;
128 Stream_Read_UINT32(s, pdu.NumFormats);
129 Stream_Read_UINT32(s, pdu.cbSizeFormatsPacket);
131 if (pdu.NumFormats == 0)
133 WLog_Print(audin->log, WLOG_ERROR,
"Sound Formats PDU contains no formats");
134 return ERROR_INVALID_DATA;
137 pdu.SoundFormats = audio_formats_new(pdu.NumFormats);
138 if (!pdu.SoundFormats)
140 WLog_Print(audin->log, WLOG_ERROR,
"Failed to allocate %u SoundFormats", pdu.NumFormats);
141 return ERROR_NOT_ENOUGH_MEMORY;
144 for (UINT32 i = 0; i < pdu.NumFormats; ++i)
148 if (!audio_format_read(s, format))
151 audio_format_print(audin->log, WLOG_DEBUG, format);
154 if (pdu.cbSizeFormatsPacket != Stream_GetPosition(s))
156 WLog_Print(audin->log, WLOG_WARN,
157 "cbSizeFormatsPacket is invalid! Expected: %u Got: %zu. Fixing size",
158 pdu.cbSizeFormatsPacket, Stream_GetPosition(s));
159 const size_t pos = Stream_GetPosition(s);
160 if (pos > UINT32_MAX)
162 WLog_Print(audin->log, WLOG_ERROR,
"Stream too long, %" PRIuz
" exceeds UINT32_MAX",
164 error = ERROR_INVALID_PARAMETER;
167 pdu.cbSizeFormatsPacket = (UINT32)pos;
170 pdu.ExtraDataSize = Stream_GetRemainingLength(s);
172 IFCALLRET(context->ReceiveFormats, error, context, &pdu);
174 WLog_Print(audin->log, WLOG_ERROR,
"context->ReceiveFormats failed with error %" PRIu32
"",
178 audio_formats_free(pdu.SoundFormats, pdu.NumFormats);
183static UINT audin_server_recv_open_reply(audin_server_context* context,
wStream* s,
186 audin_server* audin = (audin_server*)context;
188 UINT error = CHANNEL_RC_OK;
190 WINPR_ASSERT(context);
191 WINPR_ASSERT(header);
193 pdu.Header = *header;
195 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
196 return ERROR_NO_DATA;
198 Stream_Read_UINT32(s, pdu.Result);
200 IFCALLRET(context->OpenReply, error, context, &pdu);
202 WLog_Print(audin->log, WLOG_ERROR,
"context->OpenReply failed with error %" PRIu32
"",
208static UINT audin_server_recv_data_incoming(audin_server_context* context,
211 audin_server* audin = (audin_server*)context;
213 UINT error = CHANNEL_RC_OK;
215 WINPR_ASSERT(context);
216 WINPR_ASSERT(header);
218 pdu.Header = *header;
220 IFCALLRET(context->IncomingData, error, context, &pdu);
222 WLog_Print(audin->log, WLOG_ERROR,
"context->IncomingData failed with error %" PRIu32
"",
228static UINT audin_server_recv_data(audin_server_context* context,
wStream* s,
231 audin_server* audin = (audin_server*)context;
234 UINT error = CHANNEL_RC_OK;
236 WINPR_ASSERT(context);
237 WINPR_ASSERT(header);
239 pdu.Header = *header;
241 pdu.Data = Stream_StaticInit(&dataBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s));
243 IFCALLRET(context->Data, error, context, &pdu);
245 WLog_Print(audin->log, WLOG_ERROR,
"context->Data failed with error %" PRIu32
"", error);
250static UINT audin_server_recv_format_change(audin_server_context* context,
wStream* s,
253 audin_server* audin = (audin_server*)context;
255 UINT error = CHANNEL_RC_OK;
257 WINPR_ASSERT(context);
258 WINPR_ASSERT(header);
260 pdu.Header = *header;
262 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
263 return ERROR_NO_DATA;
265 Stream_Read_UINT32(s, pdu.NewFormat);
267 IFCALLRET(context->ReceiveFormatChange, error, context, &pdu);
269 WLog_Print(audin->log, WLOG_ERROR,
270 "context->ReceiveFormatChange failed with error %" PRIu32
"", error);
275static DWORD WINAPI audin_server_thread_func(LPVOID arg)
280 HANDLE events[8] = { 0 };
282 HANDLE ChannelEvent = NULL;
283 DWORD BytesReturned = 0;
284 audin_server* audin = (audin_server*)arg;
285 UINT error = CHANNEL_RC_OK;
286 DWORD status = ERROR_INTERNAL_ERROR;
290 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer,
291 &BytesReturned) == TRUE)
293 if (BytesReturned ==
sizeof(HANDLE))
294 ChannelEvent = *(HANDLE*)buffer;
296 WTSFreeMemory(buffer);
300 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelQuery failed");
301 error = ERROR_INTERNAL_ERROR;
306 events[nCount++] = audin->stopEvent;
307 events[nCount++] = ChannelEvent;
313 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
315 if (status == WAIT_FAILED)
317 error = GetLastError();
318 WLog_Print(audin->log, WLOG_ERROR,
319 "WaitForMultipleObjects failed with error %" PRIu32
"", error);
322 if (status == WAIT_OBJECT_0)
325 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
326 &BytesReturned) == FALSE)
328 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelQuery failed");
329 error = ERROR_INTERNAL_ERROR;
333 ready = *((BOOL*)buffer);
334 WTSFreeMemory(buffer);
340 s = Stream_New(NULL, 4096);
344 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
345 error = CHANNEL_RC_NO_MEMORY;
353 version.Version = audin->context.serverVersion;
355 if ((error = audin->context.SendVersion(&audin->context, &version)))
357 WLog_Print(audin->log, WLOG_ERROR,
"SendVersion failed with error %" PRIu32
"!", error);
366 if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
369 if (status == WAIT_FAILED)
371 error = GetLastError();
372 WLog_Print(audin->log, WLOG_ERROR,
373 "WaitForMultipleObjects failed with error %" PRIu32
"", error);
376 if (status == WAIT_OBJECT_0)
379 Stream_SetPosition(s, 0);
381 if (!WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned))
383 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelRead failed!");
384 error = ERROR_INTERNAL_ERROR;
388 if (BytesReturned < 1)
391 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
394 WINPR_ASSERT(Stream_Capacity(s) <= UINT32_MAX);
395 if (WTSVirtualChannelRead(audin->audin_channel, 0, Stream_BufferAs(s,
char),
396 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
398 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelRead failed!");
399 error = ERROR_INTERNAL_ERROR;
403 Stream_SetLength(s, BytesReturned);
404 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, SNDIN_HEADER_SIZE))
406 error = ERROR_INTERNAL_ERROR;
410 Stream_Read_UINT8(s, header.MessageId);
412 switch (header.MessageId)
414 case MSG_SNDIN_VERSION:
415 error = audin_server_recv_version(&audin->context, s, &header);
417 case MSG_SNDIN_FORMATS:
418 error = audin_server_recv_formats(&audin->context, s, &header);
420 case MSG_SNDIN_OPEN_REPLY:
421 error = audin_server_recv_open_reply(&audin->context, s, &header);
423 case MSG_SNDIN_DATA_INCOMING:
424 error = audin_server_recv_data_incoming(&audin->context, s, &header);
427 error = audin_server_recv_data(&audin->context, s, &header);
429 case MSG_SNDIN_FORMATCHANGE:
430 error = audin_server_recv_format_change(&audin->context, s, &header);
433 WLog_Print(audin->log, WLOG_ERROR,
434 "audin_server_thread_func: unknown or invalid MessageId %" PRIu8
"",
436 error = ERROR_INVALID_DATA;
444 Stream_Free(s, TRUE);
446 (void)WTSVirtualChannelClose(audin->audin_channel);
447 audin->audin_channel = NULL;
449 if (error && audin->context.rdpcontext)
450 setChannelError(audin->context.rdpcontext, error,
451 "audin_server_thread_func reported an error");
457static BOOL audin_server_open(audin_server_context* context)
459 audin_server* audin = (audin_server*)context;
464 PULONG pSessionId = NULL;
465 DWORD BytesReturned = 0;
466 audin->SessionId = WTS_CURRENT_SESSION;
467 UINT32 channelId = 0;
470 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
471 (LPSTR*)&pSessionId, &BytesReturned))
473 audin->SessionId = (DWORD)*pSessionId;
474 WTSFreeMemory(pSessionId);
477 audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
478 WTS_CHANNEL_OPTION_DYNAMIC);
480 if (!audin->audin_channel)
482 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelOpenEx failed!");
486 channelId = WTSChannelGetIdByHandle(audin->audin_channel);
488 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
491 WLog_Print(audin->log, WLOG_ERROR,
"context->ChannelIdAssigned failed!");
495 if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
497 WLog_Print(audin->log, WLOG_ERROR,
"CreateEvent failed!");
501 if (!(audin->thread =
502 CreateThread(NULL, 0, audin_server_thread_func, (
void*)audin, 0, NULL)))
504 WLog_Print(audin->log, WLOG_ERROR,
"CreateThread failed!");
505 (void)CloseHandle(audin->stopEvent);
506 audin->stopEvent = NULL;
513 WLog_Print(audin->log, WLOG_ERROR,
"thread already running!");
517static BOOL audin_server_is_open(audin_server_context* context)
519 audin_server* audin = (audin_server*)context;
522 return audin->thread != NULL;
525static BOOL audin_server_close(audin_server_context* context)
527 audin_server* audin = (audin_server*)context;
532 (void)SetEvent(audin->stopEvent);
534 if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
536 WLog_Print(audin->log, WLOG_ERROR,
"WaitForSingleObject failed with error %" PRIu32
"",
541 (void)CloseHandle(audin->thread);
542 (void)CloseHandle(audin->stopEvent);
543 audin->thread = NULL;
544 audin->stopEvent = NULL;
547 if (audin->audin_channel)
549 (void)WTSVirtualChannelClose(audin->audin_channel);
550 audin->audin_channel = NULL;
553 audin->audin_negotiated_format = NULL;
558static wStream* audin_server_packet_new(wLog* log,
size_t size, BYTE MessageId)
563 wStream* s = Stream_New(NULL, size + SNDIN_HEADER_SIZE);
566 WLog_Print(log, WLOG_ERROR,
"Stream_New failed!");
570 Stream_Write_UINT8(s, MessageId);
575static UINT audin_server_packet_send(audin_server_context* context,
wStream* s)
577 audin_server* audin = (audin_server*)context;
578 UINT error = CHANNEL_RC_OK;
581 WINPR_ASSERT(context);
584 const size_t pos = Stream_GetPosition(s);
585 WINPR_ASSERT(pos <= UINT32_MAX);
586 if (!WTSVirtualChannelWrite(audin->audin_channel, Stream_BufferAs(s,
char), (UINT32)pos,
589 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelWrite failed!");
590 error = ERROR_INTERNAL_ERROR;
594 if (written < Stream_GetPosition(s))
596 WLog_Print(audin->log, WLOG_WARN,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"",
597 written, Stream_GetPosition(s));
601 Stream_Free(s, TRUE);
605static UINT audin_server_send_version(audin_server_context* context,
const SNDIN_VERSION* version)
607 audin_server* audin = (audin_server*)context;
609 WINPR_ASSERT(context);
610 WINPR_ASSERT(version);
612 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_VERSION);
614 return ERROR_NOT_ENOUGH_MEMORY;
616 Stream_Write_UINT32(s, version->Version);
618 return audin_server_packet_send(context, s);
621static UINT audin_server_send_formats(audin_server_context* context,
const SNDIN_FORMATS* formats)
623 audin_server* audin = (audin_server*)context;
626 WINPR_ASSERT(formats);
628 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18, MSG_SNDIN_FORMATS);
630 return ERROR_NOT_ENOUGH_MEMORY;
632 Stream_Write_UINT32(s, formats->NumFormats);
633 Stream_Write_UINT32(s, formats->cbSizeFormatsPacket);
635 for (UINT32 i = 0; i < formats->NumFormats; ++i)
639 if (!audio_format_write(s, format))
641 WLog_Print(audin->log, WLOG_ERROR,
"Failed to write audio format");
642 Stream_Free(s, TRUE);
643 return CHANNEL_RC_NO_MEMORY;
647 return audin_server_packet_send(context, s);
650static UINT audin_server_send_open(audin_server_context* context,
const SNDIN_OPEN* open)
652 audin_server* audin = (audin_server*)context;
656 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18 + 22, MSG_SNDIN_OPEN);
658 return ERROR_NOT_ENOUGH_MEMORY;
660 Stream_Write_UINT32(s, open->FramesPerPacket);
661 Stream_Write_UINT32(s, open->initialFormat);
663 Stream_Write_UINT16(s, open->captureFormat.wFormatTag);
664 Stream_Write_UINT16(s, open->captureFormat.nChannels);
665 Stream_Write_UINT32(s, open->captureFormat.nSamplesPerSec);
666 Stream_Write_UINT32(s, open->captureFormat.nAvgBytesPerSec);
667 Stream_Write_UINT16(s, open->captureFormat.nBlockAlign);
668 Stream_Write_UINT16(s, open->captureFormat.wBitsPerSample);
670 if (open->ExtraFormatData)
672 Stream_Write_UINT16(s, 22);
674 Stream_Write_UINT16(s, open->ExtraFormatData->Samples.wReserved);
675 Stream_Write_UINT32(s, open->ExtraFormatData->dwChannelMask);
677 Stream_Write_UINT32(s, open->ExtraFormatData->SubFormat.Data1);
678 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data2);
679 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data3);
680 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[0]);
681 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[1]);
682 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[2]);
683 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[3]);
684 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[4]);
685 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[5]);
686 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[6]);
687 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[7]);
691 WINPR_ASSERT(open->captureFormat.wFormatTag != WAVE_FORMAT_EXTENSIBLE);
693 Stream_Write_UINT16(s, 0);
696 return audin_server_packet_send(context, s);
699static UINT audin_server_send_format_change(audin_server_context* context,
702 audin_server* audin = (audin_server*)context;
704 WINPR_ASSERT(context);
705 WINPR_ASSERT(format_change);
707 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_FORMATCHANGE);
709 return ERROR_NOT_ENOUGH_MEMORY;
711 Stream_Write_UINT32(s, format_change->NewFormat);
713 return audin_server_packet_send(context, s);
716static UINT audin_server_receive_version_default(audin_server_context* audin_ctx,
719 audin_server* audin = (audin_server*)audin_ctx;
723 WINPR_ASSERT(version);
725 if (version->Version == 0)
727 WLog_Print(audin->log, WLOG_ERROR,
"Received invalid AUDIO_INPUT version from client");
728 return ERROR_INVALID_DATA;
731 WLog_Print(audin->log, WLOG_DEBUG,
"AUDIO_INPUT version of client: %u", version->Version);
733 formats.NumFormats = audin->audin_n_server_formats;
734 formats.SoundFormats = audin->audin_server_formats;
736 return audin->context.SendFormats(&audin->context, &formats);
739static UINT send_open(audin_server* audin)
745 open.FramesPerPacket = 441;
746 open.initialFormat = audin->audin_client_format_idx;
747 open.captureFormat.wFormatTag = WAVE_FORMAT_PCM;
748 open.captureFormat.nChannels = 2;
749 open.captureFormat.nSamplesPerSec = 44100;
750 open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2;
751 open.captureFormat.nBlockAlign = 4;
752 open.captureFormat.wBitsPerSample = 16;
754 WINPR_ASSERT(audin->context.SendOpen);
755 return audin->context.SendOpen(&audin->context, &open);
758static UINT audin_server_receive_formats_default(audin_server_context* context,
761 audin_server* audin = (audin_server*)context;
763 WINPR_ASSERT(formats);
765 if (audin->audin_negotiated_format)
767 WLog_Print(audin->log, WLOG_ERROR,
768 "Received client formats, but negotiation was already done");
769 return ERROR_INVALID_DATA;
772 for (UINT32 i = 0; i < audin->audin_n_server_formats; ++i)
774 for (UINT32 j = 0; j < formats->NumFormats; ++j)
776 if (audio_format_compatible(&audin->audin_server_formats[i], &formats->SoundFormats[j]))
778 audin->audin_negotiated_format = &audin->audin_server_formats[i];
779 audin->audin_client_format_idx = i;
780 return send_open(audin);
785 WLog_Print(audin->log, WLOG_ERROR,
"Could not agree on a audio format with the server");
787 return ERROR_INVALID_DATA;
790static UINT audin_server_receive_format_change_default(audin_server_context* context,
793 audin_server* audin = (audin_server*)context;
796 WINPR_ASSERT(format_change);
798 if (format_change->NewFormat != audin->audin_client_format_idx)
800 WLog_Print(audin->log, WLOG_ERROR,
801 "NewFormat in FormatChange differs from requested format");
802 return ERROR_INVALID_DATA;
805 WLog_Print(audin->log, WLOG_DEBUG,
"Received Format Change PDU: %u", format_change->NewFormat);
807 return CHANNEL_RC_OK;
811audin_server_incoming_data_default(audin_server_context* context,
814 audin_server* audin = (audin_server*)context;
816 WINPR_ASSERT(data_incoming);
819 WLog_Print(audin->log, WLOG_DEBUG,
"Received Incoming Data PDU");
820 return CHANNEL_RC_OK;
823static UINT audin_server_open_reply_default(audin_server_context* context,
826 audin_server* audin = (audin_server*)context;
828 WINPR_ASSERT(open_reply);
831 WLog_Print(audin->log, WLOG_DEBUG,
"Open Reply PDU: Result: %" PRIu32, open_reply->Result);
832 return CHANNEL_RC_OK;
835audin_server_context* audin_server_context_new(HANDLE vcm)
837 audin_server* audin = (audin_server*)calloc(1,
sizeof(audin_server));
841 WLog_ERR(AUDIN_TAG,
"calloc failed!");
844 audin->log = WLog_Get(AUDIN_TAG);
845 audin->context.vcm = vcm;
846 audin->context.Open = audin_server_open;
847 audin->context.IsOpen = audin_server_is_open;
848 audin->context.Close = audin_server_close;
850 audin->context.SendVersion = audin_server_send_version;
851 audin->context.SendFormats = audin_server_send_formats;
852 audin->context.SendOpen = audin_server_send_open;
853 audin->context.SendFormatChange = audin_server_send_format_change;
856 audin->context.serverVersion = SNDIN_VERSION_Version_2;
857 audin->context.ReceiveVersion = audin_server_receive_version_default;
858 audin->context.ReceiveFormats = audin_server_receive_formats_default;
859 audin->context.ReceiveFormatChange = audin_server_receive_format_change_default;
860 audin->context.IncomingData = audin_server_incoming_data_default;
861 audin->context.OpenReply = audin_server_open_reply_default;
863 return &audin->context;
866void audin_server_context_free(audin_server_context* context)
868 audin_server* audin = (audin_server*)context;
873 audin_server_close(context);
874 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
875 audin->audin_server_formats = NULL;
879BOOL audin_server_set_formats(audin_server_context* context, SSIZE_T count,
882 audin_server* audin = (audin_server*)context;
885 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
886 audin->audin_n_server_formats = 0;
887 audin->audin_server_formats = NULL;
888 audin->audin_negotiated_format = NULL;
892 const size_t audin_n_server_formats =
893 server_audin_get_formats(&audin->audin_server_formats);
894 WINPR_ASSERT(audin_n_server_formats <= UINT32_MAX);
896 audin->audin_n_server_formats = (UINT32)audin_n_server_formats;
900 const size_t scount = (size_t)count;
901 AUDIO_FORMAT* audin_server_formats = audio_formats_new(scount);
902 if (!audin_server_formats)
905 for (SSIZE_T x = 0; x < count; x++)
907 if (!audio_format_copy(&formats[x], &audin_server_formats[x]))
909 audio_formats_free(audin_server_formats, scount);
914 WINPR_ASSERT(count <= UINT32_MAX);
915 audin->audin_server_formats = audin_server_formats;
916 audin->audin_n_server_formats = (UINT32)count;
918 return audin->audin_n_server_formats > 0;
921const AUDIO_FORMAT* audin_server_get_negotiated_format(
const audin_server_context* context)
923 const audin_server* audin = (
const audin_server*)context;
926 return audin->audin_negotiated_format;