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))
84 Stream_Read_UINT32(s, pdu.Version);
86 IFCALLRET(context->ReceiveVersion, error, context, &pdu);
88 WLog_Print(audin->log, WLOG_ERROR,
"context->ReceiveVersion failed with error %" PRIu32
"",
94static UINT audin_server_recv_formats(audin_server_context* context,
wStream* s,
97 audin_server* audin = (audin_server*)context;
99 UINT error = CHANNEL_RC_OK;
101 WINPR_ASSERT(context);
102 WINPR_ASSERT(header);
104 pdu.Header = *header;
107 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4 + 4 + 18))
108 return ERROR_NO_DATA;
110 Stream_Read_UINT32(s, pdu.NumFormats);
111 Stream_Read_UINT32(s, pdu.cbSizeFormatsPacket);
113 if (pdu.NumFormats == 0)
115 WLog_Print(audin->log, WLOG_ERROR,
"Sound Formats PDU contains no formats");
116 return ERROR_INVALID_DATA;
119 pdu.SoundFormats = audio_formats_new(pdu.NumFormats);
120 if (!pdu.SoundFormats)
122 WLog_Print(audin->log, WLOG_ERROR,
"Failed to allocate %u SoundFormats", pdu.NumFormats);
123 return ERROR_NOT_ENOUGH_MEMORY;
126 for (UINT32 i = 0; i < pdu.NumFormats; ++i)
130 if (!audio_format_read(s, format))
132 WLog_Print(audin->log, WLOG_ERROR,
"Failed to read audio format");
133 audio_formats_free(pdu.SoundFormats, i + i);
134 return ERROR_INVALID_DATA;
137 audio_format_print(audin->log, WLOG_DEBUG, format);
140 if (pdu.cbSizeFormatsPacket != Stream_GetPosition(s))
142 WLog_Print(audin->log, WLOG_WARN,
143 "cbSizeFormatsPacket is invalid! Expected: %u Got: %zu. Fixing size",
144 pdu.cbSizeFormatsPacket, Stream_GetPosition(s));
145 const size_t pos = Stream_GetPosition(s);
146 if (pos > UINT32_MAX)
148 WLog_Print(audin->log, WLOG_ERROR,
"Stream too long, %" PRIuz
" exceeds UINT32_MAX",
150 error = ERROR_INVALID_PARAMETER;
153 pdu.cbSizeFormatsPacket = (UINT32)pos;
156 pdu.ExtraDataSize = Stream_GetRemainingLength(s);
158 IFCALLRET(context->ReceiveFormats, error, context, &pdu);
160 WLog_Print(audin->log, WLOG_ERROR,
"context->ReceiveFormats failed with error %" PRIu32
"",
164 audio_formats_free(pdu.SoundFormats, pdu.NumFormats);
169static UINT audin_server_recv_open_reply(audin_server_context* context,
wStream* s,
172 audin_server* audin = (audin_server*)context;
174 UINT error = CHANNEL_RC_OK;
176 WINPR_ASSERT(context);
177 WINPR_ASSERT(header);
179 pdu.Header = *header;
181 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
182 return ERROR_NO_DATA;
184 Stream_Read_UINT32(s, pdu.Result);
186 IFCALLRET(context->OpenReply, error, context, &pdu);
188 WLog_Print(audin->log, WLOG_ERROR,
"context->OpenReply failed with error %" PRIu32
"",
194static UINT audin_server_recv_data_incoming(audin_server_context* context,
197 audin_server* audin = (audin_server*)context;
199 UINT error = CHANNEL_RC_OK;
201 WINPR_ASSERT(context);
202 WINPR_ASSERT(header);
204 pdu.Header = *header;
206 IFCALLRET(context->IncomingData, error, context, &pdu);
208 WLog_Print(audin->log, WLOG_ERROR,
"context->IncomingData failed with error %" PRIu32
"",
214static UINT audin_server_recv_data(audin_server_context* context,
wStream* s,
217 audin_server* audin = (audin_server*)context;
220 UINT error = CHANNEL_RC_OK;
222 WINPR_ASSERT(context);
223 WINPR_ASSERT(header);
225 pdu.Header = *header;
227 pdu.Data = Stream_StaticInit(&dataBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s));
229 IFCALLRET(context->Data, error, context, &pdu);
231 WLog_Print(audin->log, WLOG_ERROR,
"context->Data failed with error %" PRIu32
"", error);
236static UINT audin_server_recv_format_change(audin_server_context* context,
wStream* s,
239 audin_server* audin = (audin_server*)context;
241 UINT error = CHANNEL_RC_OK;
243 WINPR_ASSERT(context);
244 WINPR_ASSERT(header);
246 pdu.Header = *header;
248 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
249 return ERROR_NO_DATA;
251 Stream_Read_UINT32(s, pdu.NewFormat);
253 IFCALLRET(context->ReceiveFormatChange, error, context, &pdu);
255 WLog_Print(audin->log, WLOG_ERROR,
256 "context->ReceiveFormatChange failed with error %" PRIu32
"", error);
261static DWORD WINAPI audin_server_thread_func(LPVOID arg)
266 HANDLE events[8] = { 0 };
268 HANDLE ChannelEvent = NULL;
269 DWORD BytesReturned = 0;
270 audin_server* audin = (audin_server*)arg;
271 UINT error = CHANNEL_RC_OK;
272 DWORD status = ERROR_INTERNAL_ERROR;
276 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer,
277 &BytesReturned) == TRUE)
279 if (BytesReturned ==
sizeof(HANDLE))
280 ChannelEvent = *(HANDLE*)buffer;
282 WTSFreeMemory(buffer);
286 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelQuery failed");
287 error = ERROR_INTERNAL_ERROR;
292 events[nCount++] = audin->stopEvent;
293 events[nCount++] = ChannelEvent;
299 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
301 if (status == WAIT_FAILED)
303 error = GetLastError();
304 WLog_Print(audin->log, WLOG_ERROR,
305 "WaitForMultipleObjects failed with error %" PRIu32
"", error);
308 if (status == WAIT_OBJECT_0)
311 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
312 &BytesReturned) == FALSE)
314 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelQuery failed");
315 error = ERROR_INTERNAL_ERROR;
319 ready = *((BOOL*)buffer);
320 WTSFreeMemory(buffer);
326 s = Stream_New(NULL, 4096);
330 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
331 error = CHANNEL_RC_NO_MEMORY;
339 version.Version = audin->context.serverVersion;
341 if ((error = audin->context.SendVersion(&audin->context, &version)))
343 WLog_Print(audin->log, WLOG_ERROR,
"SendVersion failed with error %" PRIu32
"!", error);
352 if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
355 if (status == WAIT_FAILED)
357 error = GetLastError();
358 WLog_Print(audin->log, WLOG_ERROR,
359 "WaitForMultipleObjects failed with error %" PRIu32
"", error);
362 if (status == WAIT_OBJECT_0)
365 Stream_SetPosition(s, 0);
367 if (!WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned))
369 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelRead failed!");
370 error = ERROR_INTERNAL_ERROR;
374 if (BytesReturned < 1)
377 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
380 WINPR_ASSERT(Stream_Capacity(s) <= UINT32_MAX);
381 if (WTSVirtualChannelRead(audin->audin_channel, 0, Stream_BufferAs(s,
char),
382 (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE)
384 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelRead failed!");
385 error = ERROR_INTERNAL_ERROR;
389 Stream_SetLength(s, BytesReturned);
390 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, SNDIN_HEADER_SIZE))
392 error = ERROR_INTERNAL_ERROR;
396 Stream_Read_UINT8(s, header.MessageId);
398 switch (header.MessageId)
400 case MSG_SNDIN_VERSION:
401 error = audin_server_recv_version(&audin->context, s, &header);
403 case MSG_SNDIN_FORMATS:
404 error = audin_server_recv_formats(&audin->context, s, &header);
406 case MSG_SNDIN_OPEN_REPLY:
407 error = audin_server_recv_open_reply(&audin->context, s, &header);
409 case MSG_SNDIN_DATA_INCOMING:
410 error = audin_server_recv_data_incoming(&audin->context, s, &header);
413 error = audin_server_recv_data(&audin->context, s, &header);
415 case MSG_SNDIN_FORMATCHANGE:
416 error = audin_server_recv_format_change(&audin->context, s, &header);
419 WLog_Print(audin->log, WLOG_ERROR,
420 "audin_server_thread_func: unknown or invalid MessageId %" PRIu8
"",
422 error = ERROR_INVALID_DATA;
430 Stream_Free(s, TRUE);
432 (void)WTSVirtualChannelClose(audin->audin_channel);
433 audin->audin_channel = NULL;
435 if (error && audin->context.rdpcontext)
436 setChannelError(audin->context.rdpcontext, error,
437 "audin_server_thread_func reported an error");
443static BOOL audin_server_open(audin_server_context* context)
445 audin_server* audin = (audin_server*)context;
450 PULONG pSessionId = NULL;
451 DWORD BytesReturned = 0;
452 audin->SessionId = WTS_CURRENT_SESSION;
453 UINT32 channelId = 0;
456 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
457 (LPSTR*)&pSessionId, &BytesReturned))
459 audin->SessionId = (DWORD)*pSessionId;
460 WTSFreeMemory(pSessionId);
463 audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
464 WTS_CHANNEL_OPTION_DYNAMIC);
466 if (!audin->audin_channel)
468 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelOpenEx failed!");
472 channelId = WTSChannelGetIdByHandle(audin->audin_channel);
474 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
477 WLog_Print(audin->log, WLOG_ERROR,
"context->ChannelIdAssigned failed!");
481 if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
483 WLog_Print(audin->log, WLOG_ERROR,
"CreateEvent failed!");
487 if (!(audin->thread =
488 CreateThread(NULL, 0, audin_server_thread_func, (
void*)audin, 0, NULL)))
490 WLog_Print(audin->log, WLOG_ERROR,
"CreateThread failed!");
491 (void)CloseHandle(audin->stopEvent);
492 audin->stopEvent = NULL;
499 WLog_Print(audin->log, WLOG_ERROR,
"thread already running!");
503static BOOL audin_server_is_open(audin_server_context* context)
505 audin_server* audin = (audin_server*)context;
508 return audin->thread != NULL;
511static BOOL audin_server_close(audin_server_context* context)
513 audin_server* audin = (audin_server*)context;
518 (void)SetEvent(audin->stopEvent);
520 if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
522 WLog_Print(audin->log, WLOG_ERROR,
"WaitForSingleObject failed with error %" PRIu32
"",
527 (void)CloseHandle(audin->thread);
528 (void)CloseHandle(audin->stopEvent);
529 audin->thread = NULL;
530 audin->stopEvent = NULL;
533 if (audin->audin_channel)
535 (void)WTSVirtualChannelClose(audin->audin_channel);
536 audin->audin_channel = NULL;
539 audin->audin_negotiated_format = NULL;
544static wStream* audin_server_packet_new(wLog* log,
size_t size, BYTE MessageId)
549 wStream* s = Stream_New(NULL, size + SNDIN_HEADER_SIZE);
552 WLog_Print(log, WLOG_ERROR,
"Stream_New failed!");
556 Stream_Write_UINT8(s, MessageId);
561static UINT audin_server_packet_send(audin_server_context* context,
wStream* s)
563 audin_server* audin = (audin_server*)context;
564 UINT error = CHANNEL_RC_OK;
567 WINPR_ASSERT(context);
570 const size_t pos = Stream_GetPosition(s);
571 WINPR_ASSERT(pos <= UINT32_MAX);
572 if (!WTSVirtualChannelWrite(audin->audin_channel, Stream_BufferAs(s,
char), (UINT32)pos,
575 WLog_Print(audin->log, WLOG_ERROR,
"WTSVirtualChannelWrite failed!");
576 error = ERROR_INTERNAL_ERROR;
580 if (written < Stream_GetPosition(s))
582 WLog_Print(audin->log, WLOG_WARN,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"",
583 written, Stream_GetPosition(s));
587 Stream_Free(s, TRUE);
591static UINT audin_server_send_version(audin_server_context* context,
const SNDIN_VERSION* version)
593 audin_server* audin = (audin_server*)context;
595 WINPR_ASSERT(context);
596 WINPR_ASSERT(version);
598 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_VERSION);
600 return ERROR_NOT_ENOUGH_MEMORY;
602 Stream_Write_UINT32(s, version->Version);
604 return audin_server_packet_send(context, s);
607static UINT audin_server_send_formats(audin_server_context* context,
const SNDIN_FORMATS* formats)
609 audin_server* audin = (audin_server*)context;
612 WINPR_ASSERT(formats);
614 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18, MSG_SNDIN_FORMATS);
616 return ERROR_NOT_ENOUGH_MEMORY;
618 Stream_Write_UINT32(s, formats->NumFormats);
619 Stream_Write_UINT32(s, formats->cbSizeFormatsPacket);
621 for (UINT32 i = 0; i < formats->NumFormats; ++i)
625 if (!audio_format_write(s, format))
627 WLog_Print(audin->log, WLOG_ERROR,
"Failed to write audio format");
628 Stream_Free(s, TRUE);
629 return CHANNEL_RC_NO_MEMORY;
633 return audin_server_packet_send(context, s);
636static UINT audin_server_send_open(audin_server_context* context,
const SNDIN_OPEN* open)
638 audin_server* audin = (audin_server*)context;
642 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18 + 22, MSG_SNDIN_OPEN);
644 return ERROR_NOT_ENOUGH_MEMORY;
646 Stream_Write_UINT32(s, open->FramesPerPacket);
647 Stream_Write_UINT32(s, open->initialFormat);
649 Stream_Write_UINT16(s, open->captureFormat.wFormatTag);
650 Stream_Write_UINT16(s, open->captureFormat.nChannels);
651 Stream_Write_UINT32(s, open->captureFormat.nSamplesPerSec);
652 Stream_Write_UINT32(s, open->captureFormat.nAvgBytesPerSec);
653 Stream_Write_UINT16(s, open->captureFormat.nBlockAlign);
654 Stream_Write_UINT16(s, open->captureFormat.wBitsPerSample);
656 if (open->ExtraFormatData)
658 Stream_Write_UINT16(s, 22);
660 Stream_Write_UINT16(s, open->ExtraFormatData->Samples.wReserved);
661 Stream_Write_UINT32(s, open->ExtraFormatData->dwChannelMask);
663 Stream_Write_UINT32(s, open->ExtraFormatData->SubFormat.Data1);
664 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data2);
665 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data3);
666 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[0]);
667 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[1]);
668 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[2]);
669 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[3]);
670 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[4]);
671 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[5]);
672 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[6]);
673 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[7]);
677 WINPR_ASSERT(open->captureFormat.wFormatTag != WAVE_FORMAT_EXTENSIBLE);
679 Stream_Write_UINT16(s, 0);
682 return audin_server_packet_send(context, s);
685static UINT audin_server_send_format_change(audin_server_context* context,
688 audin_server* audin = (audin_server*)context;
690 WINPR_ASSERT(context);
691 WINPR_ASSERT(format_change);
693 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_FORMATCHANGE);
695 return ERROR_NOT_ENOUGH_MEMORY;
697 Stream_Write_UINT32(s, format_change->NewFormat);
699 return audin_server_packet_send(context, s);
702static UINT audin_server_receive_version_default(audin_server_context* audin_ctx,
705 audin_server* audin = (audin_server*)audin_ctx;
709 WINPR_ASSERT(version);
711 if (version->Version == 0)
713 WLog_Print(audin->log, WLOG_ERROR,
"Received invalid AUDIO_INPUT version from client");
714 return ERROR_INVALID_DATA;
717 WLog_Print(audin->log, WLOG_DEBUG,
"AUDIO_INPUT version of client: %u", version->Version);
719 formats.NumFormats = audin->audin_n_server_formats;
720 formats.SoundFormats = audin->audin_server_formats;
722 return audin->context.SendFormats(&audin->context, &formats);
725static UINT send_open(audin_server* audin)
731 open.FramesPerPacket = 441;
732 open.initialFormat = audin->audin_client_format_idx;
733 open.captureFormat.wFormatTag = WAVE_FORMAT_PCM;
734 open.captureFormat.nChannels = 2;
735 open.captureFormat.nSamplesPerSec = 44100;
736 open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2;
737 open.captureFormat.nBlockAlign = 4;
738 open.captureFormat.wBitsPerSample = 16;
740 WINPR_ASSERT(audin->context.SendOpen);
741 return audin->context.SendOpen(&audin->context, &open);
744static UINT audin_server_receive_formats_default(audin_server_context* context,
747 audin_server* audin = (audin_server*)context;
749 WINPR_ASSERT(formats);
751 if (audin->audin_negotiated_format)
753 WLog_Print(audin->log, WLOG_ERROR,
754 "Received client formats, but negotiation was already done");
755 return ERROR_INVALID_DATA;
758 for (UINT32 i = 0; i < audin->audin_n_server_formats; ++i)
760 for (UINT32 j = 0; j < formats->NumFormats; ++j)
762 if (audio_format_compatible(&audin->audin_server_formats[i], &formats->SoundFormats[j]))
764 audin->audin_negotiated_format = &audin->audin_server_formats[i];
765 audin->audin_client_format_idx = i;
766 return send_open(audin);
771 WLog_Print(audin->log, WLOG_ERROR,
"Could not agree on a audio format with the server");
773 return ERROR_INVALID_DATA;
776static UINT audin_server_receive_format_change_default(audin_server_context* context,
779 audin_server* audin = (audin_server*)context;
782 WINPR_ASSERT(format_change);
784 if (format_change->NewFormat != audin->audin_client_format_idx)
786 WLog_Print(audin->log, WLOG_ERROR,
787 "NewFormat in FormatChange differs from requested format");
788 return ERROR_INVALID_DATA;
791 WLog_Print(audin->log, WLOG_DEBUG,
"Received Format Change PDU: %u", format_change->NewFormat);
793 return CHANNEL_RC_OK;
797audin_server_incoming_data_default(audin_server_context* context,
800 audin_server* audin = (audin_server*)context;
802 WINPR_ASSERT(data_incoming);
805 WLog_Print(audin->log, WLOG_DEBUG,
"Received Incoming Data PDU");
806 return CHANNEL_RC_OK;
809static UINT audin_server_open_reply_default(audin_server_context* context,
812 audin_server* audin = (audin_server*)context;
814 WINPR_ASSERT(open_reply);
817 WLog_Print(audin->log, WLOG_DEBUG,
"Open Reply PDU: Result: %i", open_reply->Result);
818 return CHANNEL_RC_OK;
821audin_server_context* audin_server_context_new(HANDLE vcm)
823 audin_server* audin = (audin_server*)calloc(1,
sizeof(audin_server));
827 WLog_ERR(AUDIN_TAG,
"calloc failed!");
830 audin->log = WLog_Get(AUDIN_TAG);
831 audin->context.vcm = vcm;
832 audin->context.Open = audin_server_open;
833 audin->context.IsOpen = audin_server_is_open;
834 audin->context.Close = audin_server_close;
836 audin->context.SendVersion = audin_server_send_version;
837 audin->context.SendFormats = audin_server_send_formats;
838 audin->context.SendOpen = audin_server_send_open;
839 audin->context.SendFormatChange = audin_server_send_format_change;
842 audin->context.serverVersion = SNDIN_VERSION_Version_2;
843 audin->context.ReceiveVersion = audin_server_receive_version_default;
844 audin->context.ReceiveFormats = audin_server_receive_formats_default;
845 audin->context.ReceiveFormatChange = audin_server_receive_format_change_default;
846 audin->context.IncomingData = audin_server_incoming_data_default;
847 audin->context.OpenReply = audin_server_open_reply_default;
849 return &audin->context;
852void audin_server_context_free(audin_server_context* context)
854 audin_server* audin = (audin_server*)context;
859 audin_server_close(context);
860 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
861 audin->audin_server_formats = NULL;
865BOOL audin_server_set_formats(audin_server_context* context, SSIZE_T count,
868 audin_server* audin = (audin_server*)context;
871 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
872 audin->audin_n_server_formats = 0;
873 audin->audin_server_formats = NULL;
874 audin->audin_negotiated_format = NULL;
878 const size_t audin_n_server_formats =
879 server_audin_get_formats(&audin->audin_server_formats);
880 WINPR_ASSERT(audin_n_server_formats <= UINT32_MAX);
882 audin->audin_n_server_formats = (UINT32)audin_n_server_formats;
886 const size_t scount = (size_t)count;
887 AUDIO_FORMAT* audin_server_formats = audio_formats_new(scount);
888 if (!audin_server_formats)
891 for (SSIZE_T x = 0; x < count; x++)
893 if (!audio_format_copy(&formats[x], &audin_server_formats[x]))
895 audio_formats_free(audin_server_formats, scount);
900 WINPR_ASSERT(count <= UINT32_MAX);
901 audin->audin_server_formats = audin_server_formats;
902 audin->audin_n_server_formats = (UINT32)count;
904 return audin->audin_n_server_formats > 0;
907const AUDIO_FORMAT* audin_server_get_negotiated_format(
const audin_server_context* context)
909 const audin_server* audin = (
const audin_server*)context;
912 return audin->audin_negotiated_format;