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