FreeRDP
Loading...
Searching...
No Matches
audin_main.c
1
23#include <freerdp/config.h>
24
25#include <errno.h>
26#include <winpr/assert.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <winpr/crt.h>
32#include <winpr/cmdline.h>
33#include <winpr/wlog.h>
34
35#include <freerdp/addin.h>
36
37#include <winpr/stream.h>
38#include <freerdp/freerdp.h>
39#include <freerdp/codec/dsp.h>
40#include <freerdp/client/channels.h>
41#include <freerdp/channels/audin.h>
42
43#include "audin_main.h"
44
45#define SNDIN_VERSION 0x02
46
47typedef enum
48{
49 MSG_SNDIN_VERSION = 0x01,
50 MSG_SNDIN_FORMATS = 0x02,
51 MSG_SNDIN_OPEN = 0x03,
52 MSG_SNDIN_OPEN_REPLY = 0x04,
53 MSG_SNDIN_DATA_INCOMING = 0x05,
54 MSG_SNDIN_DATA = 0x06,
55 MSG_SNDIN_FORMATCHANGE = 0x07,
56} MSG_SNDIN;
57
58typedef struct
59{
60 IWTSVirtualChannelCallback iface;
61
62 IWTSPlugin* plugin;
63 IWTSVirtualChannelManager* channel_mgr;
64 IWTSVirtualChannel* channel;
65
71 AUDIO_FORMAT* formats;
72 UINT32 formats_count;
73} AUDIN_CHANNEL_CALLBACK;
74
75typedef struct
76{
77 IWTSPlugin iface;
78
79 GENERIC_LISTENER_CALLBACK* listener_callback;
80
81 /* Parsed plugin data */
82 AUDIO_FORMAT* fixed_format;
83 char* subsystem;
84 char* device_name;
85
86 /* Device interface */
87 IAudinDevice* device;
88
89 rdpContext* rdpcontext;
90 BOOL attached;
91 wStream* data;
92 AUDIO_FORMAT* format;
93 UINT32 FramesPerPacket;
94
95 FREERDP_DSP_CONTEXT* dsp_context;
96 wLog* log;
97
98 IWTSListener* listener;
99
100 BOOL initialized;
101 UINT32 version;
102 BOOL connected;
103} AUDIN_PLUGIN;
104
105static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args);
106
107static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
108 BOOL freeStream)
109{
110 if (!callback || !out)
111 return ERROR_INVALID_PARAMETER;
112
113 if (!callback->channel || !callback->channel->Write)
114 return ERROR_INTERNAL_ERROR;
115
116 Stream_SealLength(out);
117
118 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
119 const UINT error =
120 callback->channel->Write(callback->channel, len, Stream_Buffer(out), nullptr);
121
122 if (freeStream)
123 Stream_Free(out, TRUE);
124
125 return error;
126}
127
133static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
134{
135 const UINT32 ClientVersion = SNDIN_VERSION;
136 UINT32 ServerVersion = 0;
137
138 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
139 return ERROR_INVALID_DATA;
140
141 Stream_Read_UINT32(s, ServerVersion);
142 WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%" PRIu32 ", ClientVersion=%" PRIu32,
143 ServerVersion, ClientVersion);
144
145 /* Do not answer server packet, we do not support the channel version. */
146 if (ServerVersion > ClientVersion)
147 {
148 WLog_Print(audin->log, WLOG_WARN,
149 "Incompatible channel version server=%" PRIu32
150 ", client supports version=%" PRIu32,
151 ServerVersion, ClientVersion);
152 return CHANNEL_RC_OK;
153 }
154 audin->version = ServerVersion;
155
156 wStream* out = Stream_New(nullptr, 5);
157
158 if (!out)
159 {
160 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
161 return ERROR_OUTOFMEMORY;
162 }
163
164 Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
165 Stream_Write_UINT32(out, ClientVersion);
166 return audin_channel_write_and_free(callback, out, TRUE);
167}
168
174static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
175{
176 BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
177
178 if (!callback || !callback->channel || !callback->channel->Write)
179 return ERROR_INTERNAL_ERROR;
180
181 return callback->channel->Write(callback->channel, 1, out_data, nullptr);
182}
183
189static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
190{
191 UINT error = ERROR_INTERNAL_ERROR;
192 UINT32 NumFormats = 0;
193 UINT32 cbSizeFormatsPacket = 0;
194
195 WINPR_ASSERT(audin);
196 WINPR_ASSERT(callback);
197
198 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
199 return ERROR_INVALID_DATA;
200
201 Stream_Read_UINT32(s, NumFormats);
202 WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %" PRIu32 "", NumFormats);
203
204 if ((NumFormats < 1) || (NumFormats > 1000))
205 {
206 WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %" PRIu32 "", NumFormats);
207 return ERROR_INVALID_DATA;
208 }
209
210 Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
211
212 audin->format = nullptr;
213 audio_formats_free(callback->formats, callback->formats_count);
214 callback->formats_count = 0;
215
216 callback->formats = audio_formats_new(NumFormats);
217
218 if (!callback->formats)
219 {
220 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
221 return ERROR_INVALID_DATA;
222 }
223
224 wStream* out = Stream_New(nullptr, 9);
225
226 if (!out)
227 {
228 error = CHANNEL_RC_NO_MEMORY;
229 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
230 goto out;
231 }
232
233 Stream_Seek(out, 9);
234
235 /* SoundFormats (variable) */
236 for (UINT32 i = 0; i < NumFormats; i++)
237 {
238 AUDIO_FORMAT format = WINPR_C_ARRAY_INIT;
239
240 if (!audio_format_read(s, &format))
241 {
242 error = ERROR_INVALID_DATA;
243 goto out;
244 }
245
246 audio_format_print(audin->log, WLOG_DEBUG, &format);
247
248 if (!audio_format_compatible(audin->fixed_format, &format))
249 {
250 audio_format_free(&format);
251 continue;
252 }
253
254 if (freerdp_dsp_supports_format(&format, TRUE) ||
255 audin->device->FormatSupported(audin->device, &format))
256 {
257 /* Store the agreed format in the corresponding index */
258 callback->formats[callback->formats_count++] = format;
259
260 if (!audio_format_write(out, &format))
261 {
262 error = CHANNEL_RC_NO_MEMORY;
263 WLog_Print(audin->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
264 goto out;
265 }
266 }
267 else
268 {
269 audio_format_free(&format);
270 }
271 }
272
273 if ((error = audin_send_incoming_data_pdu(callback)))
274 {
275 WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
276 goto out;
277 }
278
279 cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
280 Stream_ResetPosition(out);
281 Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
282 Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
283 Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
284 if (!Stream_SetPosition(out, cbSizeFormatsPacket))
285 goto out;
286 error = audin_channel_write_and_free(callback, out, FALSE);
287out:
288
289 if (error != CHANNEL_RC_OK)
290 {
291 audin->format = nullptr;
292 audio_formats_free(callback->formats, NumFormats);
293 callback->formats = nullptr;
294 }
295
296 Stream_Free(out, TRUE);
297 return error;
298}
299
305static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
306 UINT32 NewFormat)
307{
308 WINPR_ASSERT(audin);
309 WINPR_ASSERT(callback);
310
311 wStream* out = Stream_New(nullptr, 5);
312
313 if (!out)
314 {
315 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
316 return CHANNEL_RC_OK;
317 }
318
319 Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
320 Stream_Write_UINT32(out, NewFormat);
321 return audin_channel_write_and_free(callback, out, TRUE);
322}
323
329static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
330 UINT32 Result)
331{
332 WINPR_ASSERT(audin);
333 WINPR_ASSERT(callback);
334
335 wStream* out = Stream_New(nullptr, 5);
336
337 if (!out)
338 {
339 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
340 return CHANNEL_RC_NO_MEMORY;
341 }
342
343 Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
344 Stream_Write_UINT32(out, Result);
345 return audin_channel_write_and_free(callback, out, TRUE);
346}
347
353static UINT audin_send_wave_data(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
354 const AUDIO_FORMAT* format, const BYTE* data, size_t size,
355 BOOL encode)
356{
357 Stream_ResetPosition(audin->data);
358
359 if (!Stream_EnsureRemainingCapacity(audin->data, 1))
360 return CHANNEL_RC_NO_MEMORY;
361
362 Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
363
364 if (!encode)
365 {
366 if (!Stream_EnsureRemainingCapacity(audin->data, size))
367 return CHANNEL_RC_NO_MEMORY;
368
369 Stream_Write(audin->data, data, size);
370 }
371 else
372 {
373 if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
374 return ERROR_INTERNAL_ERROR;
375 }
376
377 /* Did not encode anything, skip this, the codec is not ready for output. */
378 if (Stream_GetPosition(audin->data) <= 1)
379 return CHANNEL_RC_OK;
380
381 audio_format_print(audin->log, WLOG_TRACE, audin->format);
382 WLog_Print(audin->log, WLOG_TRACE, "[%" PRIuz "/%" PRIuz "]", size,
383 Stream_GetPosition(audin->data) - 1);
384
385 const UINT error = audin_send_incoming_data_pdu(callback);
386 if (error)
387 {
388 WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
389 return error;
390 }
391
392 return audin_channel_write_and_free(callback, audin->data, FALSE);
393}
394
395static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size,
396 void* user_data)
397{
398 WINPR_ASSERT(format);
399
400 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
401
402 if (!callback)
403 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
404
405 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
406
407 if (!audin)
408 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
409
410 if (!audin->attached)
411 return CHANNEL_RC_OK;
412
413 const BOOL compatible = audio_format_compatible(format, audin->format);
414 if (compatible && audin->device->FormatSupported(audin->device, audin->format))
415 return audin_send_wave_data(audin, callback, format, data, size, FALSE);
416
417 /* Encode at most one packet of input at a time and send each packet on its own: the
418 * device may deliver any amount, and codecs that collect samples into packets (Opus)
419 * would otherwise fall behind. */
420 const size_t frame_bytes = 1ull * format->nChannels * format->wBitsPerSample / 8;
421 size_t chunk = size;
422 if ((frame_bytes > 0) && (audin->FramesPerPacket > 0) && (audin->format->nSamplesPerSec > 0))
423 {
424 const size_t frames =
425 1ull * audin->FramesPerPacket * format->nSamplesPerSec / audin->format->nSamplesPerSec;
426 if (frames > 0)
427 chunk = frames * frame_bytes;
428 }
429
430 for (size_t offset = 0; offset < size; offset += chunk)
431 {
432 const UINT error = audin_send_wave_data(audin, callback, format, &data[offset],
433 MIN(chunk, size - offset), TRUE);
434 if (error)
435 return error;
436 }
437 return CHANNEL_RC_OK;
438}
439
440static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
441{
442 UINT error = ERROR_INTERNAL_ERROR;
443 AUDIO_FORMAT format = WINPR_C_ARRAY_INIT;
444
445 if (!audin || !audin->device)
446 return FALSE;
447
448 format = *audin->format;
449 const BOOL supported =
450 IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
451 WLog_Print(audin->log, WLOG_DEBUG, "microphone uses %s codec",
452 audio_format_get_tag_string(format.wFormatTag));
453
454 if (!supported)
455 {
456 /* Default sample rates supported by most backends. */
457 const UINT32 samplerates[] = { format.nSamplesPerSec, 96000, 48000, 44100, 22050 };
458 BOOL test = FALSE;
459
460 format.wFormatTag = WAVE_FORMAT_PCM;
461 format.wBitsPerSample = 16;
462 format.cbSize = 0;
463 for (size_t x = 0; x < ARRAYSIZE(samplerates); x++)
464 {
465 format.nSamplesPerSec = samplerates[x];
466 for (UINT16 y = audin->format->nChannels; y > 0; y--)
467 {
468 format.nChannels = y;
469 format.nBlockAlign = 2 * format.nChannels;
470 test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
471 if (test)
472 break;
473 }
474 if (test)
475 break;
476 }
477 if (!test)
478 return FALSE;
479 }
480
481 IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
482
483 if (error != CHANNEL_RC_OK)
484 {
485 WLog_ERR(TAG, "SetFormat failed with errorcode %" PRIu32 "", error);
486 return FALSE;
487 }
488
489 if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format, audin->FramesPerPacket))
490 return FALSE;
491
492 IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
493
494 if (error != CHANNEL_RC_OK)
495 {
496 WLog_ERR(TAG, "Open failed with errorcode %" PRIu32 "", error);
497 return FALSE;
498 }
499
500 return TRUE;
501}
502
508static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
509{
510 UINT32 initialFormat = 0;
511 UINT32 FramesPerPacket = 0;
512 UINT error = CHANNEL_RC_OK;
513
514 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
515 return ERROR_INVALID_DATA;
516
517 Stream_Read_UINT32(s, FramesPerPacket);
518 Stream_Read_UINT32(s, initialFormat);
519 WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
520 FramesPerPacket, initialFormat);
521
522 if (initialFormat >= callback->formats_count)
523 {
524 WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
525 initialFormat, callback->formats_count);
526 return ERROR_INVALID_DATA;
527 }
528
529 /* The RDP protocol field allows UINT32_MAX, but most backend API use INT32 as input parameter
530 * or do some math with it, so ensure that the value does not exceed reasonable limits */
531 if (FramesPerPacket >= INT32_MAX)
532 {
533 WLog_Print(audin->log, WLOG_ERROR, "invalid frames per packet %" PRIu32, FramesPerPacket);
534 return ERROR_INVALID_DATA;
535 }
536
537 audin->FramesPerPacket = FramesPerPacket;
538 audin->format = &callback->formats[initialFormat];
539
540 if (!audin_open_device(audin, callback))
541 {
542 audin->format = nullptr;
543 return ERROR_INTERNAL_ERROR;
544 }
545
546 if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
547 {
548 WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
549 return error;
550 }
551
552 if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
553 WLog_Print(audin->log, WLOG_ERROR, "audin_send_open_reply_pdu failed!");
554
555 return error;
556}
557
563/* Two formats of the same stream: same codec, channels, sample rate, sample size, block alignment
564 * and codec data. Only the bit rate (nAvgBytesPerSec) may differ. */
565static BOOL audin_same_stream(const AUDIO_FORMAT* a, const AUDIO_FORMAT* b)
566{
567 if (!a || !b)
568 return FALSE;
569
570 if ((a->wFormatTag != b->wFormatTag) || (a->nChannels != b->nChannels) ||
571 (a->nSamplesPerSec != b->nSamplesPerSec) || (a->nBlockAlign != b->nBlockAlign) ||
572 (a->wBitsPerSample != b->wBitsPerSample) || (a->cbSize != b->cbSize))
573 return FALSE;
574
575 if (a->cbSize == 0)
576 return TRUE;
577
578 if (!a->data || !b->data)
579 return FALSE;
580
581 return memcmp(a->data, b->data, a->cbSize) == 0;
582}
583
584static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
585 wStream* s)
586{
587 UINT32 NewFormat = 0;
588 UINT error = CHANNEL_RC_OK;
589
590 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
591 return ERROR_INVALID_DATA;
592
593 Stream_Read_UINT32(s, NewFormat);
594 WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%" PRIu32 "", NewFormat);
595
596 if (NewFormat >= callback->formats_count)
597 {
598 WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
599 NewFormat, callback->formats_count);
600 return ERROR_INVALID_DATA;
601 }
602
603 const AUDIO_FORMAT* previous = audin->format;
604 audin->format = &callback->formats[NewFormat];
605
606 /* The server follows the line by changing the bit rate of the same codec (Windows offers eight
607 * AAC formats that differ only there), several times a second on a busy session. Closing and
608 * opening the device and resetting the encoder for each change dropped the audio queued in
609 * both, and the microphone came through with gaps: within the same stream the capture and the
610 * encoder go on. */
611 if (audin->device && audin_same_stream(previous, audin->format))
612 {
613 WLog_Print(audin->log, WLOG_DEBUG, "only the bit rate changed, the capture goes on");
614 }
615 else
616 {
617 if (audin->device)
618 {
619 IFCALLRET(audin->device->Close, error, audin->device);
620
621 if (error != CHANNEL_RC_OK)
622 {
623 WLog_ERR(TAG, "Close failed with errorcode %" PRIu32 "", error);
624 return error;
625 }
626 }
627
628 if (!audin_open_device(audin, callback))
629 {
630 audin->format = nullptr;
631 return ERROR_INTERNAL_ERROR;
632 }
633 }
634
635 if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
636 WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
637
638 return error;
639}
640
646static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
647{
648 UINT error = 0;
649 BYTE MessageId = 0;
650 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
651
652 if (!callback || !data)
653 return ERROR_INVALID_PARAMETER;
654
655 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
656
657 if (!audin)
658 return ERROR_INTERNAL_ERROR;
659
660 if (!Stream_CheckAndLogRequiredLength(TAG, data, 1))
661 return ERROR_NO_DATA;
662
663 Stream_Read_UINT8(data, MessageId);
664 WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02" PRIx8 "", MessageId);
665
666 switch (MessageId)
667 {
668 case MSG_SNDIN_VERSION:
669 error = audin_process_version(audin, callback, data);
670 break;
671
672 case MSG_SNDIN_FORMATS:
673 error = audin_process_formats(audin, callback, data);
674 break;
675
676 case MSG_SNDIN_OPEN:
677 error = audin_process_open(audin, callback, data);
678 break;
679
680 case MSG_SNDIN_FORMATCHANGE:
681 error = audin_process_format_change(audin, callback, data);
682 break;
683
684 default:
685 WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02" PRIx8 "", MessageId);
686 error = ERROR_INVALID_DATA;
687 break;
688 }
689
690 return error;
691}
692
698static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
699{
700 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
701 WINPR_ASSERT(callback);
702
703 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
704 WINPR_ASSERT(audin);
705
706 UINT error = CHANNEL_RC_OK;
707 WLog_Print(audin->log, WLOG_TRACE, "...");
708
709 if (audin->device)
710 {
711 IFCALLRET(audin->device->Close, error, audin->device);
712
713 if (error != CHANNEL_RC_OK)
714 WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %" PRIu32 "", error);
715 }
716
717 audin->format = nullptr;
718 audio_formats_free(callback->formats, callback->formats_count);
719 free(callback);
720 audin->connected = FALSE;
721 return error;
722}
723
729static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
730 IWTSVirtualChannel* pChannel,
731 WINPR_ATTR_UNUSED BYTE* Data,
732 WINPR_ATTR_UNUSED BOOL* pbAccept,
733 IWTSVirtualChannelCallback** ppCallback)
734{
735 GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
736
737 if (!listener_callback || !listener_callback->plugin)
738 return ERROR_INTERNAL_ERROR;
739
740 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
741 WLog_Print(audin->log, WLOG_TRACE, "...");
742
743 if (audin->connected)
744 {
745 WLog_Print(audin->log, WLOG_ERROR, "Channel already connected, terminating.");
746 return ERROR_DEVICE_ALREADY_ATTACHED;
747 }
748
749 AUDIN_CHANNEL_CALLBACK* callback =
750 (AUDIN_CHANNEL_CALLBACK*)calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
751
752 if (!callback)
753 {
754 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
755 return CHANNEL_RC_NO_MEMORY;
756 }
757
758 callback->iface.OnDataReceived = audin_on_data_received;
759 callback->iface.OnClose = audin_on_close;
760 callback->plugin = listener_callback->plugin;
761 callback->channel_mgr = listener_callback->channel_mgr;
762 callback->channel = pChannel;
763 *ppCallback = &callback->iface;
764 audin->connected = TRUE;
765 return CHANNEL_RC_OK;
766}
767
773static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
774{
775 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
776
777 if (!audin)
778 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
779
780 if (!pChannelMgr)
781 return ERROR_INVALID_PARAMETER;
782
783 if (audin->initialized)
784 {
785 WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
786 return ERROR_INVALID_DATA;
787 }
788
789 WLog_Print(audin->log, WLOG_TRACE, "...");
790 audin->listener_callback =
792
793 if (!audin->listener_callback)
794 {
795 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
796 return CHANNEL_RC_NO_MEMORY;
797 }
798
799 audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
800 audin->listener_callback->plugin = pPlugin;
801 audin->listener_callback->channel_mgr = pChannelMgr;
802 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
803 &audin->listener_callback->iface, &audin->listener);
804
805 audin->initialized = rc == CHANNEL_RC_OK;
806 return rc;
807}
808
814static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
815{
816 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
817 UINT error = CHANNEL_RC_OK;
818
819 if (!audin)
820 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
821
822 WLog_Print(audin->log, WLOG_TRACE, "...");
823
824 if (audin->listener_callback)
825 {
826 IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
827 if (mgr)
828 IFCALL(mgr->DestroyListener, mgr, audin->listener);
829 }
830 audio_formats_free(audin->fixed_format, 1);
831
832 if (audin->device)
833 {
834 IFCALLRET(audin->device->Free, error, audin->device);
835
836 if (error != CHANNEL_RC_OK)
837 {
838 WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %" PRIu32 "", error);
839 // don't stop on error
840 }
841
842 audin->device = nullptr;
843 }
844
845 freerdp_dsp_context_free(audin->dsp_context);
846 Stream_Free(audin->data, TRUE);
847 free(audin->subsystem);
848 free(audin->device_name);
849 free(audin->listener_callback);
850 free(audin);
851 return CHANNEL_RC_OK;
852}
853
854static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
855{
856 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
857 UINT error = CHANNEL_RC_OK;
858
859 if (!audin)
860 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
861
862 audin->attached = TRUE;
863 return error;
864}
865
866static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
867{
868 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
869 UINT error = CHANNEL_RC_OK;
870
871 if (!audin)
872 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
873
874 audin->attached = FALSE;
875 return error;
876}
877
883static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
884{
885 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
886
887 WINPR_ASSERT(audin);
888
889 if (audin->device)
890 {
891 WLog_Print(audin->log, WLOG_ERROR, "existing device, abort.");
892 return ERROR_ALREADY_EXISTS;
893 }
894
895 WLog_Print(audin->log, WLOG_DEBUG, "device registered.");
896 audin->device = device;
897 return CHANNEL_RC_OK;
898}
899
905static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, const ADDIN_ARGV* args)
906{
907 WINPR_ASSERT(audin);
908
909 FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
910 UINT error = ERROR_INTERNAL_ERROR;
911
912 PVIRTUALCHANNELENTRY pvce =
913 freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name, nullptr, 0);
914 PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
915
916 if (entry == nullptr)
917 {
918 WLog_Print(audin->log, WLOG_ERROR,
919 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
920 name);
921 return ERROR_INVALID_FUNCTION;
922 }
923
924 entryPoints.plugin = &audin->iface;
925 entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
926 entryPoints.args = args;
927 entryPoints.rdpcontext = audin->rdpcontext;
928
929 error = entry(&entryPoints);
930 if (error)
931 {
932 WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
933 return error;
934 }
935
936 WLog_Print(audin->log, WLOG_INFO, "Loaded %s backend for audin", name);
937 return CHANNEL_RC_OK;
938}
939
945static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
946{
947 WINPR_ASSERT(audin);
948
949 free(audin->subsystem);
950 audin->subsystem = _strdup(subsystem);
951
952 if (!audin->subsystem)
953 {
954 WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
955 return ERROR_NOT_ENOUGH_MEMORY;
956 }
957
958 return CHANNEL_RC_OK;
959}
960
966static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
967{
968 WINPR_ASSERT(audin);
969
970 free(audin->device_name);
971 audin->device_name = _strdup(device_name);
972
973 if (!audin->device_name)
974 {
975 WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
976 return ERROR_NOT_ENOUGH_MEMORY;
977 }
978
979 return CHANNEL_RC_OK;
980}
981
982BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
983{
984 COMMAND_LINE_ARGUMENT_A audin_args[] = {
985 { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", nullptr, nullptr, -1, nullptr,
986 "subsystem" },
987 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr, "device" },
988 { "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", nullptr, nullptr, -1, nullptr,
989 "format" },
990 { "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", nullptr, nullptr, -1, nullptr, "rate" },
991 { "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", nullptr, nullptr, -1, nullptr,
992 "channel" },
993 { nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
994 };
995
996 if (!args || args->argc == 1)
997 return TRUE;
998
999 const DWORD flags =
1000 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
1001 const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin,
1002 nullptr, nullptr);
1003
1004 if (status != 0)
1005 return FALSE;
1006
1007 const COMMAND_LINE_ARGUMENT_A* arg = audin_args;
1008 errno = 0;
1009
1010 do
1011 {
1012 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
1013 continue;
1014
1015 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
1016 {
1017 const UINT error = audin_set_subsystem(audin, arg->Value);
1018 if (error != CHANNEL_RC_OK)
1019 {
1020 WLog_Print(audin->log, WLOG_ERROR,
1021 "audin_set_subsystem failed with error %" PRIu32 "!", error);
1022 return FALSE;
1023 }
1024 }
1025 CommandLineSwitchCase(arg, "dev")
1026 {
1027 const UINT error = audin_set_device_name(audin, arg->Value);
1028 if (error != CHANNEL_RC_OK)
1029 {
1030 WLog_Print(audin->log, WLOG_ERROR,
1031 "audin_set_device_name failed with error %" PRIu32 "!", error);
1032 return FALSE;
1033 }
1034 }
1035 CommandLineSwitchCase(arg, "format")
1036 {
1037 unsigned long val = strtoul(arg->Value, nullptr, 0);
1038
1039 if ((errno != 0) || (val > UINT16_MAX))
1040 return FALSE;
1041
1042 audin->fixed_format->wFormatTag = (UINT16)val;
1043 }
1044 CommandLineSwitchCase(arg, "rate")
1045 {
1046 unsigned long val = strtoul(arg->Value, nullptr, 0);
1047
1048 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
1049 return FALSE;
1050
1051 audin->fixed_format->nSamplesPerSec = (UINT32)val;
1052 }
1053 CommandLineSwitchCase(arg, "channel")
1054 {
1055 unsigned long val = strtoul(arg->Value, nullptr, 0);
1056
1057 if ((errno != 0) || (val <= UINT16_MAX))
1058 audin->fixed_format->nChannels = (UINT16)val;
1059 }
1060 CommandLineSwitchDefault(arg)
1061 {
1062 }
1063 CommandLineSwitchEnd(arg)
1064 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
1065
1066 return TRUE;
1067}
1068
1074FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
1075{
1076 struct SubsystemEntry
1077 {
1078 char* subsystem;
1079 char* device;
1080 };
1081 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
1082 struct SubsystemEntry entries[] = {
1083#if defined(WITH_PULSE)
1084 { "pulse", "" },
1085#endif
1086#if defined(WITH_OSS)
1087 { "oss", "default" },
1088#endif
1089#if defined(WITH_ALSA)
1090 { "alsa", "default" },
1091#endif
1092#if defined(WITH_AAUDIO)
1093 { "aaudio", "default" },
1094#endif
1095#if defined(WITH_OPENSLES)
1096 { "opensles", "default" },
1097#endif
1098#if defined(WITH_WINMM)
1099 { "winmm", "default" },
1100#endif
1101#if defined(WITH_MACAUDIO)
1102 { "mac", "default" },
1103#endif
1104#if defined(WITH_IOSAUDIO)
1105 { "ios", "default" },
1106#endif
1107#if defined(WITH_SNDIO)
1108 { "sndio", "default" },
1109#endif
1110 { nullptr, nullptr }
1111 };
1112 struct SubsystemEntry* entry = &entries[0];
1113 WINPR_ASSERT(pEntryPoints);
1114 WINPR_ASSERT(pEntryPoints->GetPlugin);
1115 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1116
1117 if (audin != nullptr)
1118 return CHANNEL_RC_ALREADY_INITIALIZED;
1119
1120 audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
1121
1122 if (!audin)
1123 {
1124 WLog_ERR(TAG, "calloc failed!");
1125 return CHANNEL_RC_NO_MEMORY;
1126 }
1127
1128 audin->log = WLog_Get(TAG);
1129 audin->data = Stream_New(nullptr, 4096);
1130 audin->fixed_format = audio_format_new();
1131
1132 if (!audin->fixed_format)
1133 goto out;
1134
1135 if (!audin->data)
1136 goto out;
1137
1138 audin->dsp_context = freerdp_dsp_context_new(TRUE);
1139
1140 if (!audin->dsp_context)
1141 goto out;
1142
1143 audin->attached = TRUE;
1144 audin->iface.Initialize = audin_plugin_initialize;
1145 audin->iface.Connected = nullptr;
1146 audin->iface.Disconnected = nullptr;
1147 audin->iface.Terminated = audin_plugin_terminated;
1148 audin->iface.Attached = audin_plugin_attached;
1149 audin->iface.Detached = audin_plugin_detached;
1150
1151 {
1152 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1153 audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1154
1155 if (args)
1156 {
1157 if (!audin_process_addin_args(audin, args))
1158 goto out;
1159 }
1160
1161 if (audin->subsystem)
1162 {
1163 if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1164 {
1165 WLog_Print(
1166 audin->log, WLOG_ERROR,
1167 "Unable to load microphone redirection subsystem %s because of error %" PRIu32
1168 "",
1169 audin->subsystem, error);
1170 goto out;
1171 }
1172 }
1173 else
1174 {
1175 while (entry && entry->subsystem && !audin->device)
1176 {
1177 if ((error = audin_set_subsystem(audin, entry->subsystem)))
1178 {
1179 WLog_Print(audin->log, WLOG_ERROR,
1180 "audin_set_subsystem for %s failed with error %" PRIu32 "!",
1181 entry->subsystem, error);
1182 }
1183 else if ((error = audin_set_device_name(audin, entry->device)))
1184 {
1185 WLog_Print(audin->log, WLOG_ERROR,
1186 "audin_set_device_name for %s failed with error %" PRIu32 "!",
1187 entry->subsystem, error);
1188 }
1189 else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1190 {
1191 WLog_Print(audin->log, WLOG_ERROR,
1192 "audin_load_device_plugin %s failed with error %" PRIu32 "!",
1193 entry->subsystem, error);
1194 }
1195
1196 entry++;
1197 }
1198 }
1199 }
1200
1201 if (audin->device == nullptr)
1202 {
1203 /* If we have no audin device do not register plugin but still return OK or the client will
1204 * just disconnect due to a missing microphone. */
1205 WLog_Print(audin->log, WLOG_ERROR, "No microphone device could be found.");
1206 error = CHANNEL_RC_OK;
1207 goto out;
1208 }
1209
1210 error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1211 if (error == CHANNEL_RC_OK)
1212 return error;
1213
1214out:
1215 audin_plugin_terminated(&audin->iface);
1216 return error;
1217}