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} AUDIN_PLUGIN;
103
104static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args);
105
106static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
107 BOOL freeStream)
108{
109 if (!callback || !out)
110 return ERROR_INVALID_PARAMETER;
111
112 if (!callback->channel || !callback->channel->Write)
113 return ERROR_INTERNAL_ERROR;
114
115 Stream_SealLength(out);
116
117 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
118 const UINT error =
119 callback->channel->Write(callback->channel, len, Stream_Buffer(out), nullptr);
120
121 if (freeStream)
122 Stream_Free(out, TRUE);
123
124 return error;
125}
126
132static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
133{
134 const UINT32 ClientVersion = SNDIN_VERSION;
135 UINT32 ServerVersion = 0;
136
137 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
138 return ERROR_INVALID_DATA;
139
140 Stream_Read_UINT32(s, ServerVersion);
141 WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%" PRIu32 ", ClientVersion=%" PRIu32,
142 ServerVersion, ClientVersion);
143
144 /* Do not answer server packet, we do not support the channel version. */
145 if (ServerVersion > ClientVersion)
146 {
147 WLog_Print(audin->log, WLOG_WARN,
148 "Incompatible channel version server=%" PRIu32
149 ", client supports version=%" PRIu32,
150 ServerVersion, ClientVersion);
151 return CHANNEL_RC_OK;
152 }
153 audin->version = ServerVersion;
154
155 wStream* out = Stream_New(nullptr, 5);
156
157 if (!out)
158 {
159 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
160 return ERROR_OUTOFMEMORY;
161 }
162
163 Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
164 Stream_Write_UINT32(out, ClientVersion);
165 return audin_channel_write_and_free(callback, out, TRUE);
166}
167
173static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
174{
175 BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
176
177 if (!callback || !callback->channel || !callback->channel->Write)
178 return ERROR_INTERNAL_ERROR;
179
180 return callback->channel->Write(callback->channel, 1, out_data, nullptr);
181}
182
188static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
189{
190 UINT error = ERROR_INTERNAL_ERROR;
191 UINT32 NumFormats = 0;
192 UINT32 cbSizeFormatsPacket = 0;
193
194 WINPR_ASSERT(audin);
195 WINPR_ASSERT(callback);
196
197 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
198 return ERROR_INVALID_DATA;
199
200 Stream_Read_UINT32(s, NumFormats);
201 WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %" PRIu32 "", NumFormats);
202
203 if ((NumFormats < 1) || (NumFormats > 1000))
204 {
205 WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %" PRIu32 "", NumFormats);
206 return ERROR_INVALID_DATA;
207 }
208
209 Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
210
211 audin->format = nullptr;
212 audio_formats_free(callback->formats, callback->formats_count);
213 callback->formats_count = 0;
214
215 callback->formats = audio_formats_new(NumFormats);
216
217 if (!callback->formats)
218 {
219 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
220 return ERROR_INVALID_DATA;
221 }
222
223 wStream* out = Stream_New(nullptr, 9);
224
225 if (!out)
226 {
227 error = CHANNEL_RC_NO_MEMORY;
228 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
229 goto out;
230 }
231
232 Stream_Seek(out, 9);
233
234 /* SoundFormats (variable) */
235 for (UINT32 i = 0; i < NumFormats; i++)
236 {
237 AUDIO_FORMAT format = WINPR_C_ARRAY_INIT;
238
239 if (!audio_format_read(s, &format))
240 {
241 error = ERROR_INVALID_DATA;
242 goto out;
243 }
244
245 audio_format_print(audin->log, WLOG_DEBUG, &format);
246
247 if (!audio_format_compatible(audin->fixed_format, &format))
248 {
249 audio_format_free(&format);
250 continue;
251 }
252
253 if (freerdp_dsp_supports_format(&format, TRUE) ||
254 audin->device->FormatSupported(audin->device, &format))
255 {
256 /* Store the agreed format in the corresponding index */
257 callback->formats[callback->formats_count++] = format;
258
259 if (!audio_format_write(out, &format))
260 {
261 error = CHANNEL_RC_NO_MEMORY;
262 WLog_Print(audin->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
263 goto out;
264 }
265 }
266 else
267 {
268 audio_format_free(&format);
269 }
270 }
271
272 if ((error = audin_send_incoming_data_pdu(callback)))
273 {
274 WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
275 goto out;
276 }
277
278 cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
279 Stream_ResetPosition(out);
280 Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
281 Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
282 Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
283 if (!Stream_SetPosition(out, cbSizeFormatsPacket))
284 goto out;
285 error = audin_channel_write_and_free(callback, out, FALSE);
286out:
287
288 if (error != CHANNEL_RC_OK)
289 {
290 audin->format = nullptr;
291 audio_formats_free(callback->formats, NumFormats);
292 callback->formats = nullptr;
293 }
294
295 Stream_Free(out, TRUE);
296 return error;
297}
298
304static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
305 UINT32 NewFormat)
306{
307 WINPR_ASSERT(audin);
308 WINPR_ASSERT(callback);
309
310 wStream* out = Stream_New(nullptr, 5);
311
312 if (!out)
313 {
314 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
315 return CHANNEL_RC_OK;
316 }
317
318 Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
319 Stream_Write_UINT32(out, NewFormat);
320 return audin_channel_write_and_free(callback, out, TRUE);
321}
322
328static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
329 UINT32 Result)
330{
331 WINPR_ASSERT(audin);
332 WINPR_ASSERT(callback);
333
334 wStream* out = Stream_New(nullptr, 5);
335
336 if (!out)
337 {
338 WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
339 return CHANNEL_RC_NO_MEMORY;
340 }
341
342 Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
343 Stream_Write_UINT32(out, Result);
344 return audin_channel_write_and_free(callback, out, TRUE);
345}
346
352static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size,
353 void* user_data)
354{
355 WINPR_ASSERT(format);
356
357 UINT error = ERROR_INTERNAL_ERROR;
358 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
359
360 if (!callback)
361 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
362
363 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
364
365 if (!audin)
366 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
367
368 if (!audin->attached)
369 return CHANNEL_RC_OK;
370
371 Stream_ResetPosition(audin->data);
372
373 if (!Stream_EnsureRemainingCapacity(audin->data, 1))
374 return CHANNEL_RC_NO_MEMORY;
375
376 Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
377
378 const BOOL compatible = audio_format_compatible(format, audin->format);
379 if (compatible && audin->device->FormatSupported(audin->device, audin->format))
380 {
381 if (!Stream_EnsureRemainingCapacity(audin->data, size))
382 return CHANNEL_RC_NO_MEMORY;
383
384 Stream_Write(audin->data, data, size);
385 }
386 else
387 {
388 if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
389 return ERROR_INTERNAL_ERROR;
390 }
391
392 /* Did not encode anything, skip this, the codec is not ready for output. */
393 if (Stream_GetPosition(audin->data) <= 1)
394 return CHANNEL_RC_OK;
395
396 audio_format_print(audin->log, WLOG_TRACE, audin->format);
397 WLog_Print(audin->log, WLOG_TRACE, "[%" PRIuz "/%" PRIuz "]", size,
398 Stream_GetPosition(audin->data) - 1);
399
400 if ((error = audin_send_incoming_data_pdu(callback)))
401 {
402 WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
403 return error;
404 }
405
406 return audin_channel_write_and_free(callback, audin->data, FALSE);
407}
408
409static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
410{
411 UINT error = ERROR_INTERNAL_ERROR;
412 AUDIO_FORMAT format = WINPR_C_ARRAY_INIT;
413
414 if (!audin || !audin->device)
415 return FALSE;
416
417 format = *audin->format;
418 const BOOL supported =
419 IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
420 WLog_Print(audin->log, WLOG_DEBUG, "microphone uses %s codec",
421 audio_format_get_tag_string(format.wFormatTag));
422
423 if (!supported)
424 {
425 /* Default sample rates supported by most backends. */
426 const UINT32 samplerates[] = { format.nSamplesPerSec, 96000, 48000, 44100, 22050 };
427 BOOL test = FALSE;
428
429 format.wFormatTag = WAVE_FORMAT_PCM;
430 format.wBitsPerSample = 16;
431 format.cbSize = 0;
432 for (size_t x = 0; x < ARRAYSIZE(samplerates); x++)
433 {
434 format.nSamplesPerSec = samplerates[x];
435 for (UINT16 y = audin->format->nChannels; y > 0; y--)
436 {
437 format.nChannels = y;
438 format.nBlockAlign = 2 * format.nChannels;
439 test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
440 if (test)
441 break;
442 }
443 if (test)
444 break;
445 }
446 if (!test)
447 return FALSE;
448 }
449
450 IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
451
452 if (error != CHANNEL_RC_OK)
453 {
454 WLog_ERR(TAG, "SetFormat failed with errorcode %" PRIu32 "", error);
455 return FALSE;
456 }
457
458 if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format, audin->FramesPerPacket))
459 return FALSE;
460
461 IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
462
463 if (error != CHANNEL_RC_OK)
464 {
465 WLog_ERR(TAG, "Open failed with errorcode %" PRIu32 "", error);
466 return FALSE;
467 }
468
469 return TRUE;
470}
471
477static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
478{
479 UINT32 initialFormat = 0;
480 UINT32 FramesPerPacket = 0;
481 UINT error = CHANNEL_RC_OK;
482
483 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
484 return ERROR_INVALID_DATA;
485
486 Stream_Read_UINT32(s, FramesPerPacket);
487 Stream_Read_UINT32(s, initialFormat);
488 WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
489 FramesPerPacket, initialFormat);
490
491 if (initialFormat >= callback->formats_count)
492 {
493 WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
494 initialFormat, callback->formats_count);
495 return ERROR_INVALID_DATA;
496 }
497
498 /* The RDP protocol field allows UINT32_MAX, but most backend API use INT32 as input parameter
499 * or do some math with it, so ensure that the value does not exceed reasonable limits */
500 if (FramesPerPacket >= INT32_MAX)
501 {
502 WLog_Print(audin->log, WLOG_ERROR, "invalid frames per packet %" PRIu32, FramesPerPacket);
503 return ERROR_INVALID_DATA;
504 }
505
506 audin->FramesPerPacket = FramesPerPacket;
507 audin->format = &callback->formats[initialFormat];
508
509 if (!audin_open_device(audin, callback))
510 return ERROR_INTERNAL_ERROR;
511
512 if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
513 {
514 WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
515 return error;
516 }
517
518 if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
519 WLog_Print(audin->log, WLOG_ERROR, "audin_send_open_reply_pdu failed!");
520
521 return error;
522}
523
529static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
530 wStream* s)
531{
532 UINT32 NewFormat = 0;
533 UINT error = CHANNEL_RC_OK;
534
535 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
536 return ERROR_INVALID_DATA;
537
538 Stream_Read_UINT32(s, NewFormat);
539 WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%" PRIu32 "", NewFormat);
540
541 if (NewFormat >= callback->formats_count)
542 {
543 WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
544 NewFormat, callback->formats_count);
545 return ERROR_INVALID_DATA;
546 }
547
548 audin->format = &callback->formats[NewFormat];
549
550 if (audin->device)
551 {
552 IFCALLRET(audin->device->Close, error, audin->device);
553
554 if (error != CHANNEL_RC_OK)
555 {
556 WLog_ERR(TAG, "Close failed with errorcode %" PRIu32 "", error);
557 return error;
558 }
559 }
560
561 if (!audin_open_device(audin, callback))
562 return ERROR_INTERNAL_ERROR;
563
564 if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
565 WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
566
567 return error;
568}
569
575static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
576{
577 UINT error = 0;
578 BYTE MessageId = 0;
579 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
580
581 if (!callback || !data)
582 return ERROR_INVALID_PARAMETER;
583
584 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
585
586 if (!audin)
587 return ERROR_INTERNAL_ERROR;
588
589 if (!Stream_CheckAndLogRequiredLength(TAG, data, 1))
590 return ERROR_NO_DATA;
591
592 Stream_Read_UINT8(data, MessageId);
593 WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02" PRIx8 "", MessageId);
594
595 switch (MessageId)
596 {
597 case MSG_SNDIN_VERSION:
598 error = audin_process_version(audin, callback, data);
599 break;
600
601 case MSG_SNDIN_FORMATS:
602 error = audin_process_formats(audin, callback, data);
603 break;
604
605 case MSG_SNDIN_OPEN:
606 error = audin_process_open(audin, callback, data);
607 break;
608
609 case MSG_SNDIN_FORMATCHANGE:
610 error = audin_process_format_change(audin, callback, data);
611 break;
612
613 default:
614 WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02" PRIx8 "", MessageId);
615 error = ERROR_INVALID_DATA;
616 break;
617 }
618
619 return error;
620}
621
627static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
628{
629 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
630 WINPR_ASSERT(callback);
631
632 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
633 WINPR_ASSERT(audin);
634
635 UINT error = CHANNEL_RC_OK;
636 WLog_Print(audin->log, WLOG_TRACE, "...");
637
638 if (audin->device)
639 {
640 IFCALLRET(audin->device->Close, error, audin->device);
641
642 if (error != CHANNEL_RC_OK)
643 WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %" PRIu32 "", error);
644 }
645
646 audin->format = nullptr;
647 audio_formats_free(callback->formats, callback->formats_count);
648 free(callback);
649 return error;
650}
651
657static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
658 IWTSVirtualChannel* pChannel,
659 WINPR_ATTR_UNUSED BYTE* Data,
660 WINPR_ATTR_UNUSED BOOL* pbAccept,
661 IWTSVirtualChannelCallback** ppCallback)
662{
663 GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
664
665 if (!listener_callback || !listener_callback->plugin)
666 return ERROR_INTERNAL_ERROR;
667
668 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
669 WLog_Print(audin->log, WLOG_TRACE, "...");
670 AUDIN_CHANNEL_CALLBACK* callback =
671 (AUDIN_CHANNEL_CALLBACK*)calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
672
673 if (!callback)
674 {
675 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
676 return CHANNEL_RC_NO_MEMORY;
677 }
678
679 callback->iface.OnDataReceived = audin_on_data_received;
680 callback->iface.OnClose = audin_on_close;
681 callback->plugin = listener_callback->plugin;
682 callback->channel_mgr = listener_callback->channel_mgr;
683 callback->channel = pChannel;
684 *ppCallback = &callback->iface;
685 return CHANNEL_RC_OK;
686}
687
693static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
694{
695 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
696
697 if (!audin)
698 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
699
700 if (!pChannelMgr)
701 return ERROR_INVALID_PARAMETER;
702
703 if (audin->initialized)
704 {
705 WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
706 return ERROR_INVALID_DATA;
707 }
708
709 WLog_Print(audin->log, WLOG_TRACE, "...");
710 audin->listener_callback =
712
713 if (!audin->listener_callback)
714 {
715 WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
716 return CHANNEL_RC_NO_MEMORY;
717 }
718
719 audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
720 audin->listener_callback->plugin = pPlugin;
721 audin->listener_callback->channel_mgr = pChannelMgr;
722 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
723 &audin->listener_callback->iface, &audin->listener);
724
725 audin->initialized = rc == CHANNEL_RC_OK;
726 return rc;
727}
728
734static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
735{
736 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
737 UINT error = CHANNEL_RC_OK;
738
739 if (!audin)
740 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
741
742 WLog_Print(audin->log, WLOG_TRACE, "...");
743
744 if (audin->listener_callback)
745 {
746 IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
747 if (mgr)
748 IFCALL(mgr->DestroyListener, mgr, audin->listener);
749 }
750 audio_formats_free(audin->fixed_format, 1);
751
752 if (audin->device)
753 {
754 IFCALLRET(audin->device->Free, error, audin->device);
755
756 if (error != CHANNEL_RC_OK)
757 {
758 WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %" PRIu32 "", error);
759 // don't stop on error
760 }
761
762 audin->device = nullptr;
763 }
764
765 freerdp_dsp_context_free(audin->dsp_context);
766 Stream_Free(audin->data, TRUE);
767 free(audin->subsystem);
768 free(audin->device_name);
769 free(audin->listener_callback);
770 free(audin);
771 return CHANNEL_RC_OK;
772}
773
774static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
775{
776 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
777 UINT error = CHANNEL_RC_OK;
778
779 if (!audin)
780 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
781
782 audin->attached = TRUE;
783 return error;
784}
785
786static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
787{
788 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
789 UINT error = CHANNEL_RC_OK;
790
791 if (!audin)
792 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
793
794 audin->attached = FALSE;
795 return error;
796}
797
803static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
804{
805 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
806
807 WINPR_ASSERT(audin);
808
809 if (audin->device)
810 {
811 WLog_Print(audin->log, WLOG_ERROR, "existing device, abort.");
812 return ERROR_ALREADY_EXISTS;
813 }
814
815 WLog_Print(audin->log, WLOG_DEBUG, "device registered.");
816 audin->device = device;
817 return CHANNEL_RC_OK;
818}
819
825static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, const ADDIN_ARGV* args)
826{
827 WINPR_ASSERT(audin);
828
829 FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
830 UINT error = ERROR_INTERNAL_ERROR;
831
832 PVIRTUALCHANNELENTRY pvce =
833 freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name, nullptr, 0);
834 PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
835
836 if (entry == nullptr)
837 {
838 WLog_Print(audin->log, WLOG_ERROR,
839 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
840 name);
841 return ERROR_INVALID_FUNCTION;
842 }
843
844 entryPoints.plugin = &audin->iface;
845 entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
846 entryPoints.args = args;
847 entryPoints.rdpcontext = audin->rdpcontext;
848
849 error = entry(&entryPoints);
850 if (error)
851 {
852 WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
853 return error;
854 }
855
856 WLog_Print(audin->log, WLOG_INFO, "Loaded %s backend for audin", name);
857 return CHANNEL_RC_OK;
858}
859
865static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
866{
867 WINPR_ASSERT(audin);
868
869 free(audin->subsystem);
870 audin->subsystem = _strdup(subsystem);
871
872 if (!audin->subsystem)
873 {
874 WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
875 return ERROR_NOT_ENOUGH_MEMORY;
876 }
877
878 return CHANNEL_RC_OK;
879}
880
886static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
887{
888 WINPR_ASSERT(audin);
889
890 free(audin->device_name);
891 audin->device_name = _strdup(device_name);
892
893 if (!audin->device_name)
894 {
895 WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
896 return ERROR_NOT_ENOUGH_MEMORY;
897 }
898
899 return CHANNEL_RC_OK;
900}
901
902BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
903{
904 COMMAND_LINE_ARGUMENT_A audin_args[] = {
905 { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", nullptr, nullptr, -1, nullptr,
906 "subsystem" },
907 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr, "device" },
908 { "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", nullptr, nullptr, -1, nullptr,
909 "format" },
910 { "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", nullptr, nullptr, -1, nullptr, "rate" },
911 { "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", nullptr, nullptr, -1, nullptr,
912 "channel" },
913 { nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
914 };
915
916 if (!args || args->argc == 1)
917 return TRUE;
918
919 const DWORD flags =
920 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
921 const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin,
922 nullptr, nullptr);
923
924 if (status != 0)
925 return FALSE;
926
927 const COMMAND_LINE_ARGUMENT_A* arg = audin_args;
928 errno = 0;
929
930 do
931 {
932 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
933 continue;
934
935 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
936 {
937 const UINT error = audin_set_subsystem(audin, arg->Value);
938 if (error != CHANNEL_RC_OK)
939 {
940 WLog_Print(audin->log, WLOG_ERROR,
941 "audin_set_subsystem failed with error %" PRIu32 "!", error);
942 return FALSE;
943 }
944 }
945 CommandLineSwitchCase(arg, "dev")
946 {
947 const UINT error = audin_set_device_name(audin, arg->Value);
948 if (error != CHANNEL_RC_OK)
949 {
950 WLog_Print(audin->log, WLOG_ERROR,
951 "audin_set_device_name failed with error %" PRIu32 "!", error);
952 return FALSE;
953 }
954 }
955 CommandLineSwitchCase(arg, "format")
956 {
957 unsigned long val = strtoul(arg->Value, nullptr, 0);
958
959 if ((errno != 0) || (val > UINT16_MAX))
960 return FALSE;
961
962 audin->fixed_format->wFormatTag = (UINT16)val;
963 }
964 CommandLineSwitchCase(arg, "rate")
965 {
966 unsigned long val = strtoul(arg->Value, nullptr, 0);
967
968 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
969 return FALSE;
970
971 audin->fixed_format->nSamplesPerSec = (UINT32)val;
972 }
973 CommandLineSwitchCase(arg, "channel")
974 {
975 unsigned long val = strtoul(arg->Value, nullptr, 0);
976
977 if ((errno != 0) || (val <= UINT16_MAX))
978 audin->fixed_format->nChannels = (UINT16)val;
979 }
980 CommandLineSwitchDefault(arg)
981 {
982 }
983 CommandLineSwitchEnd(arg)
984 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
985
986 return TRUE;
987}
988
994FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
995{
996 struct SubsystemEntry
997 {
998 char* subsystem;
999 char* device;
1000 };
1001 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
1002 struct SubsystemEntry entries[] = {
1003#if defined(WITH_PULSE)
1004 { "pulse", "" },
1005#endif
1006#if defined(WITH_OSS)
1007 { "oss", "default" },
1008#endif
1009#if defined(WITH_ALSA)
1010 { "alsa", "default" },
1011#endif
1012#if defined(WITH_OPENSLES)
1013 { "opensles", "default" },
1014#endif
1015#if defined(WITH_WINMM)
1016 { "winmm", "default" },
1017#endif
1018#if defined(WITH_MACAUDIO)
1019 { "mac", "default" },
1020#endif
1021#if defined(WITH_IOSAUDIO)
1022 { "ios", "default" },
1023#endif
1024#if defined(WITH_SNDIO)
1025 { "sndio", "default" },
1026#endif
1027 { nullptr, nullptr }
1028 };
1029 struct SubsystemEntry* entry = &entries[0];
1030 WINPR_ASSERT(pEntryPoints);
1031 WINPR_ASSERT(pEntryPoints->GetPlugin);
1032 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1033
1034 if (audin != nullptr)
1035 return CHANNEL_RC_ALREADY_INITIALIZED;
1036
1037 audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
1038
1039 if (!audin)
1040 {
1041 WLog_ERR(TAG, "calloc failed!");
1042 return CHANNEL_RC_NO_MEMORY;
1043 }
1044
1045 audin->log = WLog_Get(TAG);
1046 audin->data = Stream_New(nullptr, 4096);
1047 audin->fixed_format = audio_format_new();
1048
1049 if (!audin->fixed_format)
1050 goto out;
1051
1052 if (!audin->data)
1053 goto out;
1054
1055 audin->dsp_context = freerdp_dsp_context_new(TRUE);
1056
1057 if (!audin->dsp_context)
1058 goto out;
1059
1060 audin->attached = TRUE;
1061 audin->iface.Initialize = audin_plugin_initialize;
1062 audin->iface.Connected = nullptr;
1063 audin->iface.Disconnected = nullptr;
1064 audin->iface.Terminated = audin_plugin_terminated;
1065 audin->iface.Attached = audin_plugin_attached;
1066 audin->iface.Detached = audin_plugin_detached;
1067
1068 {
1069 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1070 audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1071
1072 if (args)
1073 {
1074 if (!audin_process_addin_args(audin, args))
1075 goto out;
1076 }
1077
1078 if (audin->subsystem)
1079 {
1080 if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1081 {
1082 WLog_Print(
1083 audin->log, WLOG_ERROR,
1084 "Unable to load microphone redirection subsystem %s because of error %" PRIu32
1085 "",
1086 audin->subsystem, error);
1087 goto out;
1088 }
1089 }
1090 else
1091 {
1092 while (entry && entry->subsystem && !audin->device)
1093 {
1094 if ((error = audin_set_subsystem(audin, entry->subsystem)))
1095 {
1096 WLog_Print(audin->log, WLOG_ERROR,
1097 "audin_set_subsystem for %s failed with error %" PRIu32 "!",
1098 entry->subsystem, error);
1099 }
1100 else if ((error = audin_set_device_name(audin, entry->device)))
1101 {
1102 WLog_Print(audin->log, WLOG_ERROR,
1103 "audin_set_device_name for %s failed with error %" PRIu32 "!",
1104 entry->subsystem, error);
1105 }
1106 else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1107 {
1108 WLog_Print(audin->log, WLOG_ERROR,
1109 "audin_load_device_plugin %s failed with error %" PRIu32 "!",
1110 entry->subsystem, error);
1111 }
1112
1113 entry++;
1114 }
1115 }
1116 }
1117
1118 if (audin->device == nullptr)
1119 {
1120 /* If we have no audin device do not register plugin but still return OK or the client will
1121 * just disconnect due to a missing microphone. */
1122 WLog_Print(audin->log, WLOG_ERROR, "No microphone device could be found.");
1123 error = CHANNEL_RC_OK;
1124 goto out;
1125 }
1126
1127 error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1128 if (error == CHANNEL_RC_OK)
1129 return error;
1130
1131out:
1132 audin_plugin_terminated(&audin->iface);
1133 return error;
1134}