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