FreeRDP
Loading...
Searching...
No Matches
audin.c
1
23#include <freerdp/config.h>
24
25#include <winpr/crt.h>
26#include <winpr/assert.h>
27#include <winpr/synch.h>
28#include <winpr/thread.h>
29#include <winpr/stream.h>
30
31#include <freerdp/freerdp.h>
32#include <freerdp/server/server-common.h>
33#include <freerdp/server/audin.h>
34#include <freerdp/channels/log.h>
35
36#define AUDIN_TAG CHANNELS_TAG("audin.server")
37
38#define SNDIN_HEADER_SIZE 1
39
40typedef enum
41{
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,
49} MSG_SNDIN;
50
51typedef struct
52{
53 audin_server_context context;
54
55 HANDLE stopEvent;
56
57 HANDLE thread;
58 void* audin_channel;
59
60 DWORD SessionId;
61
62 AUDIO_FORMAT* audin_server_formats;
63 UINT32 audin_n_server_formats;
64 AUDIO_FORMAT* audin_negotiated_format;
65 UINT32 audin_client_format_idx;
66 wLog* log;
67} audin_server;
68
69static UINT audin_server_recv_version(audin_server_context* context, wStream* s,
70 const SNDIN_PDU* header)
71{
72 audin_server* audin = (audin_server*)context;
73 SNDIN_VERSION pdu = WINPR_C_ARRAY_INIT;
74 UINT error = CHANNEL_RC_OK;
75
76 WINPR_ASSERT(context);
77 WINPR_ASSERT(header);
78
79 pdu.Header = *header;
80
81 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
82 return ERROR_NO_DATA;
83
84 {
85 const UINT32 version = Stream_Get_UINT32(s);
86 switch (version)
87 {
88 case SNDIN_VERSION_Version_1:
89 pdu.Version = SNDIN_VERSION_Version_1;
90 break;
91 case SNDIN_VERSION_Version_2:
92 pdu.Version = SNDIN_VERSION_Version_2;
93 break;
94 default:
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);
100 break;
101 }
102 }
103
104 IFCALLRET(context->ReceiveVersion, error, context, &pdu);
105 if (error)
106 WLog_Print(audin->log, WLOG_ERROR, "context->ReceiveVersion failed with error %" PRIu32 "",
107 error);
108
109 return error;
110}
111
112static UINT audin_server_recv_formats(audin_server_context* context, wStream* s,
113 const SNDIN_PDU* header)
114{
115 audin_server* audin = (audin_server*)context;
116 SNDIN_FORMATS pdu = WINPR_C_ARRAY_INIT;
117 UINT error = CHANNEL_RC_OK;
118
119 WINPR_ASSERT(context);
120 WINPR_ASSERT(header);
121
122 pdu.Header = *header;
123
124 /* Implementations MUST, at a minimum, support WAVE_FORMAT_PCM (0x0001) */
125 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4 + 4 + 18))
126 return ERROR_NO_DATA;
127
128 Stream_Read_UINT32(s, pdu.NumFormats);
129 Stream_Read_UINT32(s, pdu.cbSizeFormatsPacket);
130
131 if (pdu.NumFormats == 0)
132 {
133 WLog_Print(audin->log, WLOG_ERROR, "Sound Formats PDU contains no formats");
134 return ERROR_INVALID_DATA;
135 }
136
137 /* basic sanity check, we need at least pdu.NumFormats * sizeof(AUDIO_FORMAT) bytes of data */
138 if (!Stream_CheckAndLogRequiredCapacityOfSizeWLog(audin->log, s, pdu.NumFormats,
139 sizeof(AUDIO_FORMAT)))
140 return ERROR_INVALID_DATA;
141
142 if (pdu.NumFormats > 512)
143 {
144 WLog_Print(audin->log, WLOG_ERROR,
145 "Sound Formats PDU contains %" PRIu32 " formats, implausible so discarding",
146 pdu.NumFormats);
147 return ERROR_INVALID_DATA;
148 }
149
150 pdu.SoundFormats = audio_formats_new(pdu.NumFormats);
151 if (!pdu.SoundFormats)
152 {
153 WLog_Print(audin->log, WLOG_ERROR, "Failed to allocate %u SoundFormats", pdu.NumFormats);
154 return ERROR_NOT_ENOUGH_MEMORY;
155 }
156
157 for (UINT32 i = 0; i < pdu.NumFormats; ++i)
158 {
159 AUDIO_FORMAT* format = &pdu.SoundFormats[i];
160
161 if (!audio_format_read(s, format))
162 {
163 WLog_Print(audin->log, WLOG_ERROR, "Failed to read audio format");
164 error = ERROR_INVALID_DATA;
165 goto fail;
166 }
167
168 audio_format_print(audin->log, WLOG_DEBUG, format);
169 }
170
171 if (pdu.cbSizeFormatsPacket != Stream_GetPosition(s))
172 {
173 WLog_Print(audin->log, WLOG_WARN,
174 "cbSizeFormatsPacket is invalid! Expected: %u Got: %zu. Fixing size",
175 pdu.cbSizeFormatsPacket, Stream_GetPosition(s));
176 const size_t pos = Stream_GetPosition(s);
177 if (pos > UINT32_MAX)
178 {
179 WLog_Print(audin->log, WLOG_ERROR, "Stream too long, %" PRIuz " exceeds UINT32_MAX",
180 pos);
181 error = ERROR_INVALID_PARAMETER;
182 goto fail;
183 }
184 pdu.cbSizeFormatsPacket = (UINT32)pos;
185 }
186
187 pdu.ExtraDataSize = Stream_GetRemainingLength(s);
188
189 IFCALLRET(context->ReceiveFormats, error, context, &pdu);
190 if (error)
191 WLog_Print(audin->log, WLOG_ERROR, "context->ReceiveFormats failed with error %" PRIu32 "",
192 error);
193
194fail:
195 audio_formats_free(pdu.SoundFormats, pdu.NumFormats);
196
197 return error;
198}
199
200static UINT audin_server_recv_open_reply(audin_server_context* context, wStream* s,
201 const SNDIN_PDU* header)
202{
203 audin_server* audin = (audin_server*)context;
204 SNDIN_OPEN_REPLY pdu = WINPR_C_ARRAY_INIT;
205 UINT error = CHANNEL_RC_OK;
206
207 WINPR_ASSERT(context);
208 WINPR_ASSERT(header);
209
210 pdu.Header = *header;
211
212 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
213 return ERROR_NO_DATA;
214
215 Stream_Read_UINT32(s, pdu.Result);
216
217 IFCALLRET(context->OpenReply, error, context, &pdu);
218 if (error)
219 WLog_Print(audin->log, WLOG_ERROR, "context->OpenReply failed with error %" PRIu32 "",
220 error);
221
222 return error;
223}
224
225static UINT audin_server_recv_data_incoming(audin_server_context* context,
226 WINPR_ATTR_UNUSED wStream* s, const SNDIN_PDU* header)
227{
228 audin_server* audin = (audin_server*)context;
229 SNDIN_DATA_INCOMING pdu = WINPR_C_ARRAY_INIT;
230 UINT error = CHANNEL_RC_OK;
231
232 WINPR_ASSERT(context);
233 WINPR_ASSERT(header);
234
235 pdu.Header = *header;
236
237 IFCALLRET(context->IncomingData, error, context, &pdu);
238 if (error)
239 WLog_Print(audin->log, WLOG_ERROR, "context->IncomingData failed with error %" PRIu32 "",
240 error);
241
242 return error;
243}
244
245static UINT audin_server_recv_data(audin_server_context* context, wStream* s,
246 const SNDIN_PDU* header)
247{
248 audin_server* audin = (audin_server*)context;
249 SNDIN_DATA pdu = WINPR_C_ARRAY_INIT;
250 wStream dataBuffer = WINPR_C_ARRAY_INIT;
251 UINT error = CHANNEL_RC_OK;
252
253 WINPR_ASSERT(context);
254 WINPR_ASSERT(header);
255
256 pdu.Header = *header;
257
258 pdu.Data = Stream_StaticInit(&dataBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s));
259
260 IFCALLRET(context->Data, error, context, &pdu);
261 if (error)
262 WLog_Print(audin->log, WLOG_ERROR, "context->Data failed with error %" PRIu32 "", error);
263
264 return error;
265}
266
267static UINT audin_server_recv_format_change(audin_server_context* context, wStream* s,
268 const SNDIN_PDU* header)
269{
270 audin_server* audin = (audin_server*)context;
271 SNDIN_FORMATCHANGE pdu = WINPR_C_ARRAY_INIT;
272 UINT error = CHANNEL_RC_OK;
273
274 WINPR_ASSERT(context);
275 WINPR_ASSERT(header);
276
277 pdu.Header = *header;
278
279 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, 4))
280 return ERROR_NO_DATA;
281
282 Stream_Read_UINT32(s, pdu.NewFormat);
283
284 IFCALLRET(context->ReceiveFormatChange, error, context, &pdu);
285 if (error)
286 WLog_Print(audin->log, WLOG_ERROR,
287 "context->ReceiveFormatChange failed with error %" PRIu32 "", error);
288
289 return error;
290}
291
292static DWORD WINAPI audin_server_thread_func(LPVOID arg)
293{
294 wStream* s = nullptr;
295 void* buffer = nullptr;
296 DWORD nCount = 0;
297 HANDLE events[8] = WINPR_C_ARRAY_INIT;
298 BOOL ready = FALSE;
299 HANDLE ChannelEvent = nullptr;
300 DWORD BytesReturned = 0;
301 audin_server* audin = (audin_server*)arg;
302 UINT error = CHANNEL_RC_OK;
303 DWORD status = ERROR_INTERNAL_ERROR;
304
305 WINPR_ASSERT(audin);
306
307 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualEventHandle, &buffer,
308 &BytesReturned) == TRUE)
309 {
310 if (BytesReturned == sizeof(HANDLE))
311 ChannelEvent = *(HANDLE*)buffer;
312
313 WTSFreeMemory(buffer);
314 }
315 else
316 {
317 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelQuery failed");
318 error = ERROR_INTERNAL_ERROR;
319 goto out;
320 }
321
322 nCount = 0;
323 events[nCount++] = audin->stopEvent;
324 events[nCount++] = ChannelEvent;
325
326 /* Wait for the client to confirm that the Audio Input dynamic channel is ready */
327
328 while (1)
329 {
330 status = WaitForMultipleObjects(nCount, events, FALSE, 100);
331
332 if (status == WAIT_FAILED)
333 {
334 error = GetLastError();
335 WLog_Print(audin->log, WLOG_ERROR,
336 "WaitForMultipleObjects failed with error %" PRIu32 "", error);
337 goto out;
338 }
339 if (status == WAIT_OBJECT_0)
340 goto out;
341
342 if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer,
343 &BytesReturned) == FALSE)
344 {
345 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelQuery failed");
346 error = ERROR_INTERNAL_ERROR;
347 goto out;
348 }
349
350 ready = *((BOOL*)buffer);
351 WTSFreeMemory(buffer);
352
353 if (ready)
354 break;
355 }
356
357 s = Stream_New(nullptr, 4096);
358
359 if (!s)
360 {
361 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
362 error = CHANNEL_RC_NO_MEMORY;
363 goto out;
364 }
365
366 if (ready)
367 {
368 SNDIN_VERSION version = WINPR_C_ARRAY_INIT;
369
370 version.Version = audin->context.serverVersion;
371
372 if ((error = audin->context.SendVersion(&audin->context, &version)))
373 {
374 WLog_Print(audin->log, WLOG_ERROR, "SendVersion failed with error %" PRIu32 "!", error);
375 goto out_capacity;
376 }
377 }
378
379 while (ready)
380 {
381 SNDIN_PDU header = WINPR_C_ARRAY_INIT;
382
383 if ((status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE)) == WAIT_OBJECT_0)
384 break;
385
386 if (status == WAIT_FAILED)
387 {
388 error = GetLastError();
389 WLog_Print(audin->log, WLOG_ERROR,
390 "WaitForMultipleObjects failed with error %" PRIu32 "", error);
391 break;
392 }
393 if (status == WAIT_OBJECT_0)
394 break;
395
396 Stream_ResetPosition(s);
397
398 if (!WTSVirtualChannelRead(audin->audin_channel, 0, nullptr, 0, &BytesReturned))
399 {
400 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
401 error = ERROR_INTERNAL_ERROR;
402 break;
403 }
404
405 if (BytesReturned < 1)
406 continue;
407
408 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
409 break;
410
411 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
412 if (WTSVirtualChannelRead(audin->audin_channel, 0, Stream_BufferAs(s, char), len,
413 &BytesReturned) == FALSE)
414 {
415 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelRead failed!");
416 error = ERROR_INTERNAL_ERROR;
417 break;
418 }
419
420 if (BytesReturned > len)
421 {
422 WLog_Print(audin->log, WLOG_ERROR,
423 "WTSVirtualChannelRead returned an invalid length, got %" PRIu32
424 ", but limit is %" PRIu32,
425 BytesReturned, len);
426 error = ERROR_INTERNAL_ERROR;
427 break;
428 }
429
430 if (!Stream_SetLength(s, BytesReturned))
431 {
432 error = ERROR_INTERNAL_ERROR;
433 break;
434 }
435
436 if (!Stream_CheckAndLogRequiredLengthWLog(audin->log, s, SNDIN_HEADER_SIZE))
437 {
438 error = ERROR_INTERNAL_ERROR;
439 break;
440 }
441
442 Stream_Read_UINT8(s, header.MessageId);
443
444 switch (header.MessageId)
445 {
446 case MSG_SNDIN_VERSION:
447 error = audin_server_recv_version(&audin->context, s, &header);
448 break;
449 case MSG_SNDIN_FORMATS:
450 error = audin_server_recv_formats(&audin->context, s, &header);
451 break;
452 case MSG_SNDIN_OPEN_REPLY:
453 error = audin_server_recv_open_reply(&audin->context, s, &header);
454 break;
455 case MSG_SNDIN_DATA_INCOMING:
456 error = audin_server_recv_data_incoming(&audin->context, s, &header);
457 break;
458 case MSG_SNDIN_DATA:
459 error = audin_server_recv_data(&audin->context, s, &header);
460 break;
461 case MSG_SNDIN_FORMATCHANGE:
462 error = audin_server_recv_format_change(&audin->context, s, &header);
463 break;
464 default:
465 WLog_Print(audin->log, WLOG_ERROR,
466 "audin_server_thread_func: unknown or invalid MessageId %" PRIu8 "",
467 header.MessageId);
468 error = ERROR_INVALID_DATA;
469 break;
470 }
471 if (error)
472 break;
473 }
474
475out_capacity:
476 Stream_Free(s, TRUE);
477out:
478 (void)WTSVirtualChannelClose(audin->audin_channel);
479 audin->audin_channel = nullptr;
480
481 if (error && audin->context.rdpcontext)
482 setChannelError(audin->context.rdpcontext, error,
483 "audin_server_thread_func reported an error");
484
485 ExitThread(error);
486 return error;
487}
488
489static BOOL audin_server_open(audin_server_context* context)
490{
491 audin_server* audin = (audin_server*)context;
492
493 WINPR_ASSERT(audin);
494 if (!audin->thread)
495 {
496 PULONG pSessionId = nullptr;
497 DWORD BytesReturned = 0;
498 audin->SessionId = WTS_CURRENT_SESSION;
499 UINT32 channelId = 0;
500 BOOL status = TRUE;
501
502 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
503 (LPSTR*)&pSessionId, &BytesReturned))
504 {
505 audin->SessionId = (DWORD)*pSessionId;
506 WTSFreeMemory(pSessionId);
507 }
508
509 audin->audin_channel = WTSVirtualChannelOpenEx(audin->SessionId, AUDIN_DVC_CHANNEL_NAME,
510 WTS_CHANNEL_OPTION_DYNAMIC);
511
512 if (!audin->audin_channel)
513 {
514 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelOpenEx failed!");
515 return FALSE;
516 }
517
518 channelId = WTSChannelGetIdByHandle(audin->audin_channel);
519
520 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
521 if (!status)
522 {
523 WLog_Print(audin->log, WLOG_ERROR, "context->ChannelIdAssigned failed!");
524 return FALSE;
525 }
526
527 if (!(audin->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
528 {
529 WLog_Print(audin->log, WLOG_ERROR, "CreateEvent failed!");
530 return FALSE;
531 }
532
533 if (!(audin->thread =
534 CreateThread(nullptr, 0, audin_server_thread_func, (void*)audin, 0, nullptr)))
535 {
536 WLog_Print(audin->log, WLOG_ERROR, "CreateThread failed!");
537 (void)CloseHandle(audin->stopEvent);
538 audin->stopEvent = nullptr;
539 return FALSE;
540 }
541
542 return TRUE;
543 }
544
545 WLog_Print(audin->log, WLOG_ERROR, "thread already running!");
546 return FALSE;
547}
548
549static BOOL audin_server_is_open(audin_server_context* context)
550{
551 audin_server* audin = (audin_server*)context;
552
553 WINPR_ASSERT(audin);
554 return audin->thread != nullptr;
555}
556
557static BOOL audin_server_close(audin_server_context* context)
558{
559 audin_server* audin = (audin_server*)context;
560 WINPR_ASSERT(audin);
561
562 if (audin->thread)
563 {
564 (void)SetEvent(audin->stopEvent);
565
566 if (WaitForSingleObject(audin->thread, INFINITE) == WAIT_FAILED)
567 {
568 WLog_Print(audin->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
569 GetLastError());
570 return FALSE;
571 }
572
573 (void)CloseHandle(audin->thread);
574 (void)CloseHandle(audin->stopEvent);
575 audin->thread = nullptr;
576 audin->stopEvent = nullptr;
577 }
578
579 if (audin->audin_channel)
580 {
581 (void)WTSVirtualChannelClose(audin->audin_channel);
582 audin->audin_channel = nullptr;
583 }
584
585 audin->audin_negotiated_format = nullptr;
586
587 return TRUE;
588}
589
590static wStream* audin_server_packet_new(wLog* log, size_t size, BYTE MessageId)
591{
592 WINPR_ASSERT(log);
593
594 /* Allocate what we need plus header bytes */
595 wStream* s = Stream_New(nullptr, size + SNDIN_HEADER_SIZE);
596 if (!s)
597 {
598 WLog_Print(log, WLOG_ERROR, "Stream_New failed!");
599 return nullptr;
600 }
601
602 Stream_Write_UINT8(s, MessageId);
603
604 return s;
605}
606
607static UINT audin_server_packet_send(audin_server_context* context, wStream* s)
608{
609 audin_server* audin = (audin_server*)context;
610 UINT error = CHANNEL_RC_OK;
611 ULONG written = 0;
612
613 WINPR_ASSERT(context);
614 WINPR_ASSERT(s);
615
616 const size_t pos = Stream_GetPosition(s);
617 WINPR_ASSERT(pos <= UINT32_MAX);
618 if (!WTSVirtualChannelWrite(audin->audin_channel, Stream_BufferAs(s, char), (UINT32)pos,
619 &written))
620 {
621 WLog_Print(audin->log, WLOG_ERROR, "WTSVirtualChannelWrite failed!");
622 error = ERROR_INTERNAL_ERROR;
623 goto out;
624 }
625
626 if (written < Stream_GetPosition(s))
627 {
628 WLog_Print(audin->log, WLOG_WARN, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "",
629 written, Stream_GetPosition(s));
630 }
631
632out:
633 Stream_Free(s, TRUE);
634 return error;
635}
636
637static UINT audin_server_send_version(audin_server_context* context, const SNDIN_VERSION* version)
638{
639 audin_server* audin = (audin_server*)context;
640
641 WINPR_ASSERT(context);
642 WINPR_ASSERT(version);
643
644 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_VERSION);
645 if (!s)
646 return ERROR_NOT_ENOUGH_MEMORY;
647
648 Stream_Write_UINT32(s, version->Version);
649
650 return audin_server_packet_send(context, s);
651}
652
653static UINT audin_server_send_formats(audin_server_context* context, const SNDIN_FORMATS* formats)
654{
655 audin_server* audin = (audin_server*)context;
656
657 WINPR_ASSERT(audin);
658 WINPR_ASSERT(formats);
659
660 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18, MSG_SNDIN_FORMATS);
661 if (!s)
662 return ERROR_NOT_ENOUGH_MEMORY;
663
664 Stream_Write_UINT32(s, formats->NumFormats);
665 Stream_Write_UINT32(s, formats->cbSizeFormatsPacket);
666
667 for (UINT32 i = 0; i < formats->NumFormats; ++i)
668 {
669 AUDIO_FORMAT* format = &formats->SoundFormats[i];
670
671 if (!audio_format_write(s, format))
672 {
673 WLog_Print(audin->log, WLOG_ERROR, "Failed to write audio format");
674 Stream_Free(s, TRUE);
675 return CHANNEL_RC_NO_MEMORY;
676 }
677 }
678
679 return audin_server_packet_send(context, s);
680}
681
682static UINT audin_server_send_open(audin_server_context* context, const SNDIN_OPEN* open)
683{
684 audin_server* audin = (audin_server*)context;
685 WINPR_ASSERT(audin);
686 WINPR_ASSERT(open);
687
688 wStream* s = audin_server_packet_new(audin->log, 4 + 4 + 18 + 22, MSG_SNDIN_OPEN);
689 if (!s)
690 return ERROR_NOT_ENOUGH_MEMORY;
691
692 Stream_Write_UINT32(s, open->FramesPerPacket);
693 Stream_Write_UINT32(s, open->initialFormat);
694
695 Stream_Write_UINT16(s, open->captureFormat.wFormatTag);
696 Stream_Write_UINT16(s, open->captureFormat.nChannels);
697 Stream_Write_UINT32(s, open->captureFormat.nSamplesPerSec);
698 Stream_Write_UINT32(s, open->captureFormat.nAvgBytesPerSec);
699 Stream_Write_UINT16(s, open->captureFormat.nBlockAlign);
700 Stream_Write_UINT16(s, open->captureFormat.wBitsPerSample);
701
702 if (open->ExtraFormatData)
703 {
704 Stream_Write_UINT16(s, 22); /* cbSize */
705
706 Stream_Write_UINT16(s, open->ExtraFormatData->Samples.wReserved);
707 Stream_Write_UINT32(s, open->ExtraFormatData->dwChannelMask);
708
709 Stream_Write_UINT32(s, open->ExtraFormatData->SubFormat.Data1);
710 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data2);
711 Stream_Write_UINT16(s, open->ExtraFormatData->SubFormat.Data3);
712 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[0]);
713 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[1]);
714 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[2]);
715 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[3]);
716 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[4]);
717 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[5]);
718 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[6]);
719 Stream_Write_UINT8(s, open->ExtraFormatData->SubFormat.Data4[7]);
720 }
721 else
722 {
723 WINPR_ASSERT(open->captureFormat.wFormatTag != WAVE_FORMAT_EXTENSIBLE);
724
725 Stream_Write_UINT16(s, 0); /* cbSize */
726 }
727
728 return audin_server_packet_send(context, s);
729}
730
731static UINT audin_server_send_format_change(audin_server_context* context,
732 const SNDIN_FORMATCHANGE* format_change)
733{
734 audin_server* audin = (audin_server*)context;
735
736 WINPR_ASSERT(context);
737 WINPR_ASSERT(format_change);
738
739 wStream* s = audin_server_packet_new(audin->log, 4, MSG_SNDIN_FORMATCHANGE);
740 if (!s)
741 return ERROR_NOT_ENOUGH_MEMORY;
742
743 Stream_Write_UINT32(s, format_change->NewFormat);
744
745 return audin_server_packet_send(context, s);
746}
747
748static UINT audin_server_receive_version_default(audin_server_context* audin_ctx,
749 const SNDIN_VERSION* version)
750{
751 audin_server* audin = (audin_server*)audin_ctx;
752 SNDIN_FORMATS formats = WINPR_C_ARRAY_INIT;
753
754 WINPR_ASSERT(audin);
755 WINPR_ASSERT(version);
756
757 if (version->Version == 0)
758 {
759 WLog_Print(audin->log, WLOG_ERROR, "Received invalid AUDIO_INPUT version from client");
760 return ERROR_INVALID_DATA;
761 }
762
763 WLog_Print(audin->log, WLOG_DEBUG, "AUDIO_INPUT version of client: %u", version->Version);
764
765 formats.NumFormats = audin->audin_n_server_formats;
766 formats.SoundFormats = audin->audin_server_formats;
767
768 return audin->context.SendFormats(&audin->context, &formats);
769}
770
771static UINT send_open(audin_server* audin)
772{
773 SNDIN_OPEN open = WINPR_C_ARRAY_INIT;
774
775 WINPR_ASSERT(audin);
776
777 open.FramesPerPacket = 441;
778 open.initialFormat = audin->audin_client_format_idx;
779 open.captureFormat.wFormatTag = WAVE_FORMAT_PCM;
780 open.captureFormat.nChannels = 2;
781 open.captureFormat.nSamplesPerSec = 44100;
782 open.captureFormat.nAvgBytesPerSec = 44100 * 2 * 2;
783 open.captureFormat.nBlockAlign = 4;
784 open.captureFormat.wBitsPerSample = 16;
785
786 WINPR_ASSERT(audin->context.SendOpen);
787 return audin->context.SendOpen(&audin->context, &open);
788}
789
790static UINT audin_server_receive_formats_default(audin_server_context* context,
791 const SNDIN_FORMATS* formats)
792{
793 audin_server* audin = (audin_server*)context;
794 WINPR_ASSERT(audin);
795 WINPR_ASSERT(formats);
796
797 if (audin->audin_negotiated_format)
798 {
799 WLog_Print(audin->log, WLOG_ERROR,
800 "Received client formats, but negotiation was already done");
801 return ERROR_INVALID_DATA;
802 }
803
804 for (UINT32 i = 0; i < audin->audin_n_server_formats; ++i)
805 {
806 for (UINT32 j = 0; j < formats->NumFormats; ++j)
807 {
808 if (audio_format_compatible(&audin->audin_server_formats[i], &formats->SoundFormats[j]))
809 {
810 audin->audin_negotiated_format = &audin->audin_server_formats[i];
811 audin->audin_client_format_idx = i;
812 return send_open(audin);
813 }
814 }
815 }
816
817 WLog_Print(audin->log, WLOG_ERROR, "Could not agree on a audio format with the server");
818
819 return ERROR_INVALID_DATA;
820}
821
822static UINT audin_server_receive_format_change_default(audin_server_context* context,
823 const SNDIN_FORMATCHANGE* format_change)
824{
825 audin_server* audin = (audin_server*)context;
826
827 WINPR_ASSERT(audin);
828 WINPR_ASSERT(format_change);
829
830 if (format_change->NewFormat != audin->audin_client_format_idx)
831 {
832 WLog_Print(audin->log, WLOG_ERROR,
833 "NewFormat in FormatChange differs from requested format");
834 return ERROR_INVALID_DATA;
835 }
836
837 WLog_Print(audin->log, WLOG_DEBUG, "Received Format Change PDU: %u", format_change->NewFormat);
838
839 return CHANNEL_RC_OK;
840}
841
842static UINT
843audin_server_incoming_data_default(audin_server_context* context,
844 WINPR_ATTR_UNUSED const SNDIN_DATA_INCOMING* data_incoming)
845{
846 audin_server* audin = (audin_server*)context;
847 WINPR_ASSERT(audin);
848 WINPR_ASSERT(data_incoming);
849
850 /* TODO: Implement bandwidth measure of clients uplink */
851 WLog_Print(audin->log, WLOG_DEBUG, "Received Incoming Data PDU");
852 return CHANNEL_RC_OK;
853}
854
855static UINT audin_server_open_reply_default(audin_server_context* context,
856 const SNDIN_OPEN_REPLY* open_reply)
857{
858 audin_server* audin = (audin_server*)context;
859 WINPR_ASSERT(audin);
860 WINPR_ASSERT(open_reply);
861
862 /* TODO: Implement failure handling */
863 WLog_Print(audin->log, WLOG_DEBUG, "Open Reply PDU: Result: %" PRIu32, open_reply->Result);
864 return CHANNEL_RC_OK;
865}
866
867audin_server_context* audin_server_context_new(HANDLE vcm)
868{
869 audin_server* audin = (audin_server*)calloc(1, sizeof(audin_server));
870
871 if (!audin)
872 {
873 WLog_ERR(AUDIN_TAG, "calloc failed!");
874 return nullptr;
875 }
876 audin->log = WLog_Get(AUDIN_TAG);
877 audin->context.vcm = vcm;
878 audin->context.Open = audin_server_open;
879 audin->context.IsOpen = audin_server_is_open;
880 audin->context.Close = audin_server_close;
881
882 audin->context.SendVersion = audin_server_send_version;
883 audin->context.SendFormats = audin_server_send_formats;
884 audin->context.SendOpen = audin_server_send_open;
885 audin->context.SendFormatChange = audin_server_send_format_change;
886
887 /* Default values */
888 audin->context.serverVersion = SNDIN_VERSION_Version_2;
889 audin->context.ReceiveVersion = audin_server_receive_version_default;
890 audin->context.ReceiveFormats = audin_server_receive_formats_default;
891 audin->context.ReceiveFormatChange = audin_server_receive_format_change_default;
892 audin->context.IncomingData = audin_server_incoming_data_default;
893 audin->context.OpenReply = audin_server_open_reply_default;
894
895 return &audin->context;
896}
897
898void audin_server_context_free(audin_server_context* context)
899{
900 audin_server* audin = (audin_server*)context;
901
902 if (!audin)
903 return;
904
905 audin_server_close(context);
906 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
907 audin->audin_server_formats = nullptr;
908 free(audin);
909}
910
911BOOL audin_server_set_formats(audin_server_context* context, SSIZE_T count,
912 const AUDIO_FORMAT* formats)
913{
914 audin_server* audin = (audin_server*)context;
915 WINPR_ASSERT(audin);
916
917 audio_formats_free(audin->audin_server_formats, audin->audin_n_server_formats);
918 audin->audin_n_server_formats = 0;
919 audin->audin_server_formats = nullptr;
920 audin->audin_negotiated_format = nullptr;
921
922 if (count < 0)
923 {
924 const size_t audin_n_server_formats =
925 server_audin_get_formats(&audin->audin_server_formats);
926 WINPR_ASSERT(audin_n_server_formats <= UINT32_MAX);
927
928 audin->audin_n_server_formats = (UINT32)audin_n_server_formats;
929 }
930 else
931 {
932 const size_t scount = (size_t)count;
933 AUDIO_FORMAT* audin_server_formats = audio_formats_new(scount);
934 if (!audin_server_formats)
935 return count == 0;
936
937 for (SSIZE_T x = 0; x < count; x++)
938 {
939 if (!audio_format_copy(&formats[x], &audin_server_formats[x]))
940 {
941 audio_formats_free(audin_server_formats, scount);
942 return FALSE;
943 }
944 }
945
946 WINPR_ASSERT(count <= UINT32_MAX);
947 audin->audin_server_formats = audin_server_formats;
948 audin->audin_n_server_formats = (UINT32)count;
949 }
950 return audin->audin_n_server_formats > 0;
951}
952
953const AUDIO_FORMAT* audin_server_get_negotiated_format(const audin_server_context* context)
954{
955 const audin_server* audin = (const audin_server*)context;
956 WINPR_ASSERT(audin);
957
958 return audin->audin_negotiated_format;
959}