23#include <freerdp/config.h>
26#include <winpr/assert.h>
32#include <winpr/cmdline.h>
33#include <winpr/wlog.h>
35#include <freerdp/addin.h>
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>
43#include "audin_main.h"
45#define SNDIN_VERSION 0x02
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,
60 IWTSVirtualChannelCallback iface;
63 IWTSVirtualChannelManager* channel_mgr;
64 IWTSVirtualChannel* channel;
73} AUDIN_CHANNEL_CALLBACK;
89 rdpContext* rdpcontext;
93 UINT32 FramesPerPacket;
95 FREERDP_DSP_CONTEXT* dsp_context;
98 IWTSListener* listener;
104static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin,
const ADDIN_ARGV* args);
106static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback,
wStream* out,
109 if (!callback || !out)
110 return ERROR_INVALID_PARAMETER;
112 if (!callback->channel || !callback->channel->Write)
113 return ERROR_INTERNAL_ERROR;
115 Stream_SealLength(out);
117 const ULONG len = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Length(out));
119 callback->channel->Write(callback->channel, len, Stream_Buffer(out),
nullptr);
122 Stream_Free(out, TRUE);
132static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
135 UINT32 ServerVersion = 0;
137 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
138 return ERROR_INVALID_DATA;
140 Stream_Read_UINT32(s, ServerVersion);
141 WLog_Print(audin->log, WLOG_DEBUG,
"ServerVersion=%" PRIu32
", ClientVersion=%" PRIu32,
142 ServerVersion, ClientVersion);
145 if (ServerVersion > ClientVersion)
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;
153 audin->version = ServerVersion;
155 wStream* out = Stream_New(
nullptr, 5);
159 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
160 return ERROR_OUTOFMEMORY;
163 Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
164 Stream_Write_UINT32(out, ClientVersion);
165 return audin_channel_write_and_free(callback, out, TRUE);
173static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
175 BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
177 if (!callback || !callback->channel || !callback->channel->Write)
178 return ERROR_INTERNAL_ERROR;
180 return callback->channel->Write(callback->channel, 1, out_data,
nullptr);
188static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
190 UINT error = ERROR_INTERNAL_ERROR;
191 UINT32 NumFormats = 0;
192 UINT32 cbSizeFormatsPacket = 0;
195 WINPR_ASSERT(callback);
197 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
198 return ERROR_INVALID_DATA;
200 Stream_Read_UINT32(s, NumFormats);
201 WLog_Print(audin->log, WLOG_DEBUG,
"NumFormats %" PRIu32
"", NumFormats);
203 if ((NumFormats < 1) || (NumFormats > 1000))
205 WLog_Print(audin->log, WLOG_ERROR,
"bad NumFormats %" PRIu32
"", NumFormats);
206 return ERROR_INVALID_DATA;
209 Stream_Seek_UINT32(s);
211 audin->format =
nullptr;
212 audio_formats_free(callback->formats, callback->formats_count);
213 callback->formats_count = 0;
215 callback->formats = audio_formats_new(NumFormats);
217 if (!callback->formats)
219 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
220 return ERROR_INVALID_DATA;
223 wStream* out = Stream_New(
nullptr, 9);
227 error = CHANNEL_RC_NO_MEMORY;
228 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
235 for (UINT32 i = 0; i < NumFormats; i++)
239 if (!audio_format_read(s, &format))
241 error = ERROR_INVALID_DATA;
245 audio_format_print(audin->log, WLOG_DEBUG, &format);
247 if (!audio_format_compatible(audin->fixed_format, &format))
249 audio_format_free(&format);
253 if (freerdp_dsp_supports_format(&format, TRUE) ||
254 audin->device->FormatSupported(audin->device, &format))
257 callback->formats[callback->formats_count++] = format;
259 if (!audio_format_write(out, &format))
261 error = CHANNEL_RC_NO_MEMORY;
262 WLog_Print(audin->log, WLOG_ERROR,
"Stream_EnsureRemainingCapacity failed!");
268 audio_format_free(&format);
272 if ((error = audin_send_incoming_data_pdu(callback)))
274 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_incoming_data_pdu failed!");
278 cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
279 Stream_ResetPosition(out);
280 Stream_Write_UINT8(out, MSG_SNDIN_FORMATS);
281 Stream_Write_UINT32(out, callback->formats_count);
282 Stream_Write_UINT32(out, cbSizeFormatsPacket);
283 if (!Stream_SetPosition(out, cbSizeFormatsPacket))
285 error = audin_channel_write_and_free(callback, out, FALSE);
288 if (error != CHANNEL_RC_OK)
290 audin->format =
nullptr;
291 audio_formats_free(callback->formats, NumFormats);
292 callback->formats =
nullptr;
295 Stream_Free(out, TRUE);
304static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
308 WINPR_ASSERT(callback);
310 wStream* out = Stream_New(
nullptr, 5);
314 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
315 return CHANNEL_RC_OK;
318 Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
319 Stream_Write_UINT32(out, NewFormat);
320 return audin_channel_write_and_free(callback, out, TRUE);
328static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
332 WINPR_ASSERT(callback);
334 wStream* out = Stream_New(
nullptr, 5);
338 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
339 return CHANNEL_RC_NO_MEMORY;
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);
352static UINT audin_receive_wave_data(
const AUDIO_FORMAT* format,
const BYTE* data,
size_t size,
355 WINPR_ASSERT(format);
357 UINT error = ERROR_INTERNAL_ERROR;
358 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
361 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
363 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
366 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
368 if (!audin->attached)
369 return CHANNEL_RC_OK;
371 Stream_ResetPosition(audin->data);
373 if (!Stream_EnsureRemainingCapacity(audin->data, 1))
374 return CHANNEL_RC_NO_MEMORY;
376 Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
378 const BOOL compatible = audio_format_compatible(format, audin->format);
379 if (compatible && audin->device->FormatSupported(audin->device, audin->format))
381 if (!Stream_EnsureRemainingCapacity(audin->data, size))
382 return CHANNEL_RC_NO_MEMORY;
384 Stream_Write(audin->data, data, size);
388 if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
389 return ERROR_INTERNAL_ERROR;
393 if (Stream_GetPosition(audin->data) <= 1)
394 return CHANNEL_RC_OK;
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);
400 if ((error = audin_send_incoming_data_pdu(callback)))
402 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_incoming_data_pdu failed!");
406 return audin_channel_write_and_free(callback, audin->data, FALSE);
409static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
411 UINT error = ERROR_INTERNAL_ERROR;
414 if (!audin || !audin->device)
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));
426 const UINT32 samplerates[] = { format.nSamplesPerSec, 96000, 48000, 44100, 22050 };
429 format.wFormatTag = WAVE_FORMAT_PCM;
430 format.wBitsPerSample = 16;
432 for (
size_t x = 0; x < ARRAYSIZE(samplerates); x++)
434 format.nSamplesPerSec = samplerates[x];
435 for (UINT16 y = audin->format->nChannels; y > 0; y--)
437 format.nChannels = y;
438 format.nBlockAlign = 2 * format.nChannels;
439 test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
450 IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
452 if (error != CHANNEL_RC_OK)
454 WLog_ERR(TAG,
"SetFormat failed with errorcode %" PRIu32
"", error);
458 if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format, audin->FramesPerPacket))
461 IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
463 if (error != CHANNEL_RC_OK)
465 WLog_ERR(TAG,
"Open failed with errorcode %" PRIu32
"", error);
477static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
479 UINT32 initialFormat = 0;
480 UINT32 FramesPerPacket = 0;
481 UINT error = CHANNEL_RC_OK;
483 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
484 return ERROR_INVALID_DATA;
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 audin->FramesPerPacket = FramesPerPacket;
492 if (initialFormat >= callback->formats_count)
494 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
495 initialFormat, callback->formats_count);
496 return ERROR_INVALID_DATA;
499 audin->format = &callback->formats[initialFormat];
501 if (!audin_open_device(audin, callback))
502 return ERROR_INTERNAL_ERROR;
504 if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
506 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_format_change_pdu failed!");
510 if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
511 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_open_reply_pdu failed!");
521static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
524 UINT32 NewFormat = 0;
525 UINT error = CHANNEL_RC_OK;
527 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
528 return ERROR_INVALID_DATA;
530 Stream_Read_UINT32(s, NewFormat);
531 WLog_Print(audin->log, WLOG_DEBUG,
"NewFormat=%" PRIu32
"", NewFormat);
533 if (NewFormat >= callback->formats_count)
535 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
536 NewFormat, callback->formats_count);
537 return ERROR_INVALID_DATA;
540 audin->format = &callback->formats[NewFormat];
544 IFCALLRET(audin->device->Close, error, audin->device);
546 if (error != CHANNEL_RC_OK)
548 WLog_ERR(TAG,
"Close failed with errorcode %" PRIu32
"", error);
553 if (!audin_open_device(audin, callback))
554 return ERROR_INTERNAL_ERROR;
556 if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
557 WLog_ERR(TAG,
"audin_send_format_change_pdu failed!");
567static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
571 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
573 if (!callback || !data)
574 return ERROR_INVALID_PARAMETER;
576 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
579 return ERROR_INTERNAL_ERROR;
581 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, 1))
582 return ERROR_NO_DATA;
584 Stream_Read_UINT8(data, MessageId);
585 WLog_Print(audin->log, WLOG_DEBUG,
"MessageId=0x%02" PRIx8
"", MessageId);
589 case MSG_SNDIN_VERSION:
590 error = audin_process_version(audin, callback, data);
593 case MSG_SNDIN_FORMATS:
594 error = audin_process_formats(audin, callback, data);
598 error = audin_process_open(audin, callback, data);
601 case MSG_SNDIN_FORMATCHANGE:
602 error = audin_process_format_change(audin, callback, data);
606 WLog_Print(audin->log, WLOG_ERROR,
"unknown MessageId=0x%02" PRIx8
"", MessageId);
607 error = ERROR_INVALID_DATA;
619static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
621 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
622 WINPR_ASSERT(callback);
624 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
627 UINT error = CHANNEL_RC_OK;
628 WLog_Print(audin->log, WLOG_TRACE,
"...");
632 IFCALLRET(audin->device->Close, error, audin->device);
634 if (error != CHANNEL_RC_OK)
635 WLog_Print(audin->log, WLOG_ERROR,
"Close failed with errorcode %" PRIu32
"", error);
638 audin->format =
nullptr;
639 audio_formats_free(callback->formats, callback->formats_count);
649static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
650 IWTSVirtualChannel* pChannel,
651 WINPR_ATTR_UNUSED BYTE* Data,
652 WINPR_ATTR_UNUSED BOOL* pbAccept,
653 IWTSVirtualChannelCallback** ppCallback)
657 if (!listener_callback || !listener_callback->plugin)
658 return ERROR_INTERNAL_ERROR;
660 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
661 WLog_Print(audin->log, WLOG_TRACE,
"...");
662 AUDIN_CHANNEL_CALLBACK* callback =
663 (AUDIN_CHANNEL_CALLBACK*)calloc(1,
sizeof(AUDIN_CHANNEL_CALLBACK));
667 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
668 return CHANNEL_RC_NO_MEMORY;
671 callback->iface.OnDataReceived = audin_on_data_received;
672 callback->iface.OnClose = audin_on_close;
673 callback->plugin = listener_callback->plugin;
674 callback->channel_mgr = listener_callback->channel_mgr;
675 callback->channel = pChannel;
676 *ppCallback = &callback->iface;
677 return CHANNEL_RC_OK;
685static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
687 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
690 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
693 return ERROR_INVALID_PARAMETER;
695 if (audin->initialized)
697 WLog_ERR(TAG,
"[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
698 return ERROR_INVALID_DATA;
701 WLog_Print(audin->log, WLOG_TRACE,
"...");
702 audin->listener_callback =
705 if (!audin->listener_callback)
707 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
708 return CHANNEL_RC_NO_MEMORY;
711 audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
712 audin->listener_callback->plugin = pPlugin;
713 audin->listener_callback->channel_mgr = pChannelMgr;
714 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
715 &audin->listener_callback->iface, &audin->listener);
717 audin->initialized = rc == CHANNEL_RC_OK;
726static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
728 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
729 UINT error = CHANNEL_RC_OK;
732 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
734 WLog_Print(audin->log, WLOG_TRACE,
"...");
736 if (audin->listener_callback)
738 IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
740 IFCALL(mgr->DestroyListener, mgr, audin->listener);
742 audio_formats_free(audin->fixed_format, 1);
746 IFCALLRET(audin->device->Free, error, audin->device);
748 if (error != CHANNEL_RC_OK)
750 WLog_Print(audin->log, WLOG_ERROR,
"Free failed with errorcode %" PRIu32
"", error);
754 audin->device =
nullptr;
757 freerdp_dsp_context_free(audin->dsp_context);
758 Stream_Free(audin->data, TRUE);
759 free(audin->subsystem);
760 free(audin->device_name);
761 free(audin->listener_callback);
763 return CHANNEL_RC_OK;
766static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
768 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
769 UINT error = CHANNEL_RC_OK;
772 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
774 audin->attached = TRUE;
778static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
780 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
781 UINT error = CHANNEL_RC_OK;
784 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
786 audin->attached = FALSE;
795static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
797 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
803 WLog_Print(audin->log, WLOG_ERROR,
"existing device, abort.");
804 return ERROR_ALREADY_EXISTS;
807 WLog_Print(audin->log, WLOG_DEBUG,
"device registered.");
808 audin->device = device;
809 return CHANNEL_RC_OK;
817static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin,
const char* name,
const ADDIN_ARGV* args)
822 UINT error = ERROR_INTERNAL_ERROR;
824 PVIRTUALCHANNELENTRY pvce =
825 freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name,
nullptr, 0);
826 PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
828 if (entry ==
nullptr)
830 WLog_Print(audin->log, WLOG_ERROR,
831 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
833 return ERROR_INVALID_FUNCTION;
836 entryPoints.plugin = &audin->iface;
837 entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
838 entryPoints.args = args;
839 entryPoints.rdpcontext = audin->rdpcontext;
841 error = entry(&entryPoints);
844 WLog_Print(audin->log, WLOG_ERROR,
"%s entry returned error %" PRIu32
".", name, error);
848 WLog_Print(audin->log, WLOG_INFO,
"Loaded %s backend for audin", name);
849 return CHANNEL_RC_OK;
857static UINT audin_set_subsystem(AUDIN_PLUGIN* audin,
const char* subsystem)
861 free(audin->subsystem);
862 audin->subsystem = _strdup(subsystem);
864 if (!audin->subsystem)
866 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
867 return ERROR_NOT_ENOUGH_MEMORY;
870 return CHANNEL_RC_OK;
878static UINT audin_set_device_name(AUDIN_PLUGIN* audin,
const char* device_name)
882 free(audin->device_name);
883 audin->device_name = _strdup(device_name);
885 if (!audin->device_name)
887 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
888 return ERROR_NOT_ENOUGH_MEMORY;
891 return CHANNEL_RC_OK;
894BOOL audin_process_addin_args(AUDIN_PLUGIN* audin,
const ADDIN_ARGV* args)
897 {
"sys", COMMAND_LINE_VALUE_REQUIRED,
"<subsystem>",
nullptr,
nullptr, -1,
nullptr,
899 {
"dev", COMMAND_LINE_VALUE_REQUIRED,
"<device>",
nullptr,
nullptr, -1,
nullptr,
"device" },
900 {
"format", COMMAND_LINE_VALUE_REQUIRED,
"<format>",
nullptr,
nullptr, -1,
nullptr,
902 {
"rate", COMMAND_LINE_VALUE_REQUIRED,
"<rate>",
nullptr,
nullptr, -1,
nullptr,
"rate" },
903 {
"channel", COMMAND_LINE_VALUE_REQUIRED,
"<channel>",
nullptr,
nullptr, -1,
nullptr,
905 {
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr }
908 if (!args || args->argc == 1)
912 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
913 const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin,
924 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
927 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"sys")
929 const UINT error = audin_set_subsystem(audin, arg->Value);
930 if (error != CHANNEL_RC_OK)
932 WLog_Print(audin->log, WLOG_ERROR,
933 "audin_set_subsystem failed with error %" PRIu32
"!", error);
937 CommandLineSwitchCase(arg,
"dev")
939 const UINT error = audin_set_device_name(audin, arg->Value);
940 if (error != CHANNEL_RC_OK)
942 WLog_Print(audin->log, WLOG_ERROR,
943 "audin_set_device_name failed with error %" PRIu32
"!", error);
947 CommandLineSwitchCase(arg,
"format")
949 unsigned long val = strtoul(arg->Value,
nullptr, 0);
951 if ((errno != 0) || (val > UINT16_MAX))
954 audin->fixed_format->wFormatTag = (UINT16)val;
956 CommandLineSwitchCase(arg,
"rate")
958 unsigned long val = strtoul(arg->Value,
nullptr, 0);
960 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
963 audin->fixed_format->nSamplesPerSec = (UINT32)val;
965 CommandLineSwitchCase(arg,
"channel")
967 unsigned long val = strtoul(arg->Value,
nullptr, 0);
969 if ((errno != 0) || (val <= UINT16_MAX))
970 audin->fixed_format->nChannels = (UINT16)val;
972 CommandLineSwitchDefault(arg)
975 CommandLineSwitchEnd(arg)
976 }
while ((arg = CommandLineFindNextArgumentA(arg)) !=
nullptr);
986FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
988 struct SubsystemEntry
993 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
994 struct SubsystemEntry entries[] = {
995#if defined(WITH_PULSE)
999 {
"oss",
"default" },
1001#if defined(WITH_ALSA)
1002 {
"alsa",
"default" },
1004#if defined(WITH_OPENSLES)
1005 {
"opensles",
"default" },
1007#if defined(WITH_WINMM)
1008 {
"winmm",
"default" },
1010#if defined(WITH_MACAUDIO)
1011 {
"mac",
"default" },
1013#if defined(WITH_IOSAUDIO)
1014 {
"ios",
"default" },
1016#if defined(WITH_SNDIO)
1017 {
"sndio",
"default" },
1019 {
nullptr,
nullptr }
1021 struct SubsystemEntry* entry = &entries[0];
1022 WINPR_ASSERT(pEntryPoints);
1023 WINPR_ASSERT(pEntryPoints->GetPlugin);
1024 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1026 if (audin !=
nullptr)
1027 return CHANNEL_RC_ALREADY_INITIALIZED;
1029 audin = (AUDIN_PLUGIN*)calloc(1,
sizeof(AUDIN_PLUGIN));
1033 WLog_ERR(TAG,
"calloc failed!");
1034 return CHANNEL_RC_NO_MEMORY;
1037 audin->log = WLog_Get(TAG);
1038 audin->data = Stream_New(
nullptr, 4096);
1039 audin->fixed_format = audio_format_new();
1041 if (!audin->fixed_format)
1047 audin->dsp_context = freerdp_dsp_context_new(TRUE);
1049 if (!audin->dsp_context)
1052 audin->attached = TRUE;
1053 audin->iface.Initialize = audin_plugin_initialize;
1054 audin->iface.Connected =
nullptr;
1055 audin->iface.Disconnected =
nullptr;
1056 audin->iface.Terminated = audin_plugin_terminated;
1057 audin->iface.Attached = audin_plugin_attached;
1058 audin->iface.Detached = audin_plugin_detached;
1061 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1062 audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1066 if (!audin_process_addin_args(audin, args))
1070 if (audin->subsystem)
1072 if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1075 audin->log, WLOG_ERROR,
1076 "Unable to load microphone redirection subsystem %s because of error %" PRIu32
1078 audin->subsystem, error);
1084 while (entry && entry->subsystem && !audin->device)
1086 if ((error = audin_set_subsystem(audin, entry->subsystem)))
1088 WLog_Print(audin->log, WLOG_ERROR,
1089 "audin_set_subsystem for %s failed with error %" PRIu32
"!",
1090 entry->subsystem, error);
1092 else if ((error = audin_set_device_name(audin, entry->device)))
1094 WLog_Print(audin->log, WLOG_ERROR,
1095 "audin_set_device_name for %s failed with error %" PRIu32
"!",
1096 entry->subsystem, error);
1098 else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1100 WLog_Print(audin->log, WLOG_ERROR,
1101 "audin_load_device_plugin %s failed with error %" PRIu32
"!",
1102 entry->subsystem, error);
1110 if (audin->device ==
nullptr)
1114 WLog_Print(audin->log, WLOG_ERROR,
"No microphone device could be found.");
1115 error = CHANNEL_RC_OK;
1119 error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1120 if (error == CHANNEL_RC_OK)
1124 audin_plugin_terminated(&audin->iface);