23 #include <freerdp/config.h>
26 #include <winpr/assert.h>
31 #include <winpr/crt.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;
104 static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin,
const ADDIN_ARGV* args);
106 static 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);
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);
121 Stream_Free(out, TRUE);
131 static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
134 UINT32 ServerVersion = 0;
136 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
137 return ERROR_INVALID_DATA;
139 Stream_Read_UINT32(s, ServerVersion);
140 WLog_Print(audin->log, WLOG_DEBUG,
"ServerVersion=%" PRIu32
", ClientVersion=%" PRIu32,
141 ServerVersion, ClientVersion);
144 if (ServerVersion > ClientVersion)
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;
152 audin->version = ServerVersion;
154 wStream* out = Stream_New(NULL, 5);
158 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
159 return ERROR_OUTOFMEMORY;
162 Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
163 Stream_Write_UINT32(out, ClientVersion);
164 return audin_channel_write_and_free(callback, out, TRUE);
172 static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
174 BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
176 if (!callback || !callback->channel || !callback->channel->Write)
177 return ERROR_INTERNAL_ERROR;
179 return callback->channel->Write(callback->channel, 1, out_data, NULL);
187 static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
189 UINT error = ERROR_INTERNAL_ERROR;
190 UINT32 NumFormats = 0;
191 UINT32 cbSizeFormatsPacket = 0;
194 WINPR_ASSERT(callback);
196 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
197 return ERROR_INVALID_DATA;
199 Stream_Read_UINT32(s, NumFormats);
200 WLog_Print(audin->log, WLOG_DEBUG,
"NumFormats %" PRIu32
"", NumFormats);
202 if ((NumFormats < 1) || (NumFormats > 1000))
204 WLog_Print(audin->log, WLOG_ERROR,
"bad NumFormats %" PRIu32
"", NumFormats);
205 return ERROR_INVALID_DATA;
208 Stream_Seek_UINT32(s);
209 callback->formats = audio_formats_new(NumFormats);
211 if (!callback->formats)
213 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
214 return ERROR_INVALID_DATA;
217 wStream* out = Stream_New(NULL, 9);
221 error = CHANNEL_RC_NO_MEMORY;
222 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
229 for (UINT32 i = 0; i < NumFormats; i++)
233 if (!audio_format_read(s, &format))
235 error = ERROR_INVALID_DATA;
239 audio_format_print(audin->log, WLOG_DEBUG, &format);
241 if (!audio_format_compatible(audin->fixed_format, &format))
243 audio_format_free(&format);
247 if (freerdp_dsp_supports_format(&format, TRUE) ||
248 audin->device->FormatSupported(audin->device, &format))
251 callback->formats[callback->formats_count++] = format;
253 if (!audio_format_write(out, &format))
255 error = CHANNEL_RC_NO_MEMORY;
256 WLog_Print(audin->log, WLOG_ERROR,
"Stream_EnsureRemainingCapacity failed!");
262 audio_format_free(&format);
266 if ((error = audin_send_incoming_data_pdu(callback)))
268 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_incoming_data_pdu failed!");
272 cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
273 Stream_SetPosition(out, 0);
274 Stream_Write_UINT8(out, MSG_SNDIN_FORMATS);
275 Stream_Write_UINT32(out, callback->formats_count);
276 Stream_Write_UINT32(out, cbSizeFormatsPacket);
277 Stream_SetPosition(out, cbSizeFormatsPacket);
278 error = audin_channel_write_and_free(callback, out, FALSE);
281 if (error != CHANNEL_RC_OK)
283 audio_formats_free(callback->formats, NumFormats);
284 callback->formats = NULL;
287 Stream_Free(out, TRUE);
296 static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
300 WINPR_ASSERT(callback);
302 wStream* out = Stream_New(NULL, 5);
306 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
307 return CHANNEL_RC_OK;
310 Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
311 Stream_Write_UINT32(out, NewFormat);
312 return audin_channel_write_and_free(callback, out, TRUE);
320 static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
324 WINPR_ASSERT(callback);
326 wStream* out = Stream_New(NULL, 5);
330 WLog_Print(audin->log, WLOG_ERROR,
"Stream_New failed!");
331 return CHANNEL_RC_NO_MEMORY;
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);
344 static UINT audin_receive_wave_data(
const AUDIO_FORMAT* format,
const BYTE* data,
size_t size,
347 WINPR_ASSERT(format);
349 UINT error = ERROR_INTERNAL_ERROR;
350 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
353 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
355 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
358 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
360 if (!audin->attached)
361 return CHANNEL_RC_OK;
363 Stream_SetPosition(audin->data, 0);
365 if (!Stream_EnsureRemainingCapacity(audin->data, 1))
366 return CHANNEL_RC_NO_MEMORY;
368 Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
370 const BOOL compatible = audio_format_compatible(format, audin->format);
371 if (compatible && audin->device->FormatSupported(audin->device, audin->format))
373 if (!Stream_EnsureRemainingCapacity(audin->data, size))
374 return CHANNEL_RC_NO_MEMORY;
376 Stream_Write(audin->data, data, size);
380 if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
381 return ERROR_INTERNAL_ERROR;
385 if (Stream_GetPosition(audin->data) <= 1)
386 return CHANNEL_RC_OK;
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);
392 if ((error = audin_send_incoming_data_pdu(callback)))
394 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_incoming_data_pdu failed!");
398 return audin_channel_write_and_free(callback, audin->data, FALSE);
401 static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
403 UINT error = ERROR_INTERNAL_ERROR;
406 if (!audin || !audin->device)
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));
418 const UINT32 samplerates[] = { format.nSamplesPerSec, 96000, 48000, 44100, 22050 };
421 format.wFormatTag = WAVE_FORMAT_PCM;
422 format.wBitsPerSample = 16;
424 for (
size_t x = 0; x < ARRAYSIZE(samplerates); x++)
426 format.nSamplesPerSec = samplerates[x];
427 for (UINT16 y = audin->format->nChannels; y > 0; y--)
429 format.nChannels = y;
430 format.nBlockAlign = 2 * format.nChannels;
431 test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
442 IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
444 if (error != CHANNEL_RC_OK)
446 WLog_ERR(TAG,
"SetFormat failed with errorcode %" PRIu32
"", error);
450 if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format, audin->FramesPerPacket))
453 IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
455 if (error != CHANNEL_RC_OK)
457 WLog_ERR(TAG,
"Open failed with errorcode %" PRIu32
"", error);
469 static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
471 UINT32 initialFormat = 0;
472 UINT32 FramesPerPacket = 0;
473 UINT error = CHANNEL_RC_OK;
475 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
476 return ERROR_INVALID_DATA;
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;
484 if (initialFormat >= callback->formats_count)
486 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
487 initialFormat, callback->formats_count);
488 return ERROR_INVALID_DATA;
491 audin->format = &callback->formats[initialFormat];
493 if (!audin_open_device(audin, callback))
494 return ERROR_INTERNAL_ERROR;
496 if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
498 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_format_change_pdu failed!");
502 if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
503 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_open_reply_pdu failed!");
513 static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
516 UINT32 NewFormat = 0;
517 UINT error = CHANNEL_RC_OK;
519 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
520 return ERROR_INVALID_DATA;
522 Stream_Read_UINT32(s, NewFormat);
523 WLog_Print(audin->log, WLOG_DEBUG,
"NewFormat=%" PRIu32
"", NewFormat);
525 if (NewFormat >= callback->formats_count)
527 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
528 NewFormat, callback->formats_count);
529 return ERROR_INVALID_DATA;
532 audin->format = &callback->formats[NewFormat];
536 IFCALLRET(audin->device->Close, error, audin->device);
538 if (error != CHANNEL_RC_OK)
540 WLog_ERR(TAG,
"Close failed with errorcode %" PRIu32
"", error);
545 if (!audin_open_device(audin, callback))
546 return ERROR_INTERNAL_ERROR;
548 if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
549 WLog_ERR(TAG,
"audin_send_format_change_pdu failed!");
559 static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
563 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
565 if (!callback || !data)
566 return ERROR_INVALID_PARAMETER;
568 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
571 return ERROR_INTERNAL_ERROR;
573 if (!Stream_CheckAndLogRequiredCapacity(TAG, data, 1))
574 return ERROR_NO_DATA;
576 Stream_Read_UINT8(data, MessageId);
577 WLog_Print(audin->log, WLOG_DEBUG,
"MessageId=0x%02" PRIx8
"", MessageId);
581 case MSG_SNDIN_VERSION:
582 error = audin_process_version(audin, callback, data);
585 case MSG_SNDIN_FORMATS:
586 error = audin_process_formats(audin, callback, data);
590 error = audin_process_open(audin, callback, data);
593 case MSG_SNDIN_FORMATCHANGE:
594 error = audin_process_format_change(audin, callback, data);
598 WLog_Print(audin->log, WLOG_ERROR,
"unknown MessageId=0x%02" PRIx8
"", MessageId);
599 error = ERROR_INVALID_DATA;
611 static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
613 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
614 WINPR_ASSERT(callback);
616 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
619 UINT error = CHANNEL_RC_OK;
620 WLog_Print(audin->log, WLOG_TRACE,
"...");
624 IFCALLRET(audin->device->Close, error, audin->device);
626 if (error != CHANNEL_RC_OK)
627 WLog_Print(audin->log, WLOG_ERROR,
"Close failed with errorcode %" PRIu32
"", error);
630 audin->format = NULL;
631 audio_formats_free(callback->formats, callback->formats_count);
641 static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
642 IWTSVirtualChannel* pChannel, BYTE* Data,
643 BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
647 if (!listener_callback || !listener_callback->plugin)
648 return ERROR_INTERNAL_ERROR;
650 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
651 WLog_Print(audin->log, WLOG_TRACE,
"...");
652 AUDIN_CHANNEL_CALLBACK* callback =
653 (AUDIN_CHANNEL_CALLBACK*)calloc(1,
sizeof(AUDIN_CHANNEL_CALLBACK));
657 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
658 return CHANNEL_RC_NO_MEMORY;
661 callback->iface.OnDataReceived = audin_on_data_received;
662 callback->iface.OnClose = audin_on_close;
663 callback->plugin = listener_callback->plugin;
664 callback->channel_mgr = listener_callback->channel_mgr;
665 callback->channel = pChannel;
666 *ppCallback = (IWTSVirtualChannelCallback*)callback;
667 return CHANNEL_RC_OK;
675 static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
677 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
680 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
683 return ERROR_INVALID_PARAMETER;
685 if (audin->initialized)
687 WLog_ERR(TAG,
"[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
688 return ERROR_INVALID_DATA;
691 WLog_Print(audin->log, WLOG_TRACE,
"...");
692 audin->listener_callback =
695 if (!audin->listener_callback)
697 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
698 return CHANNEL_RC_NO_MEMORY;
701 audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
702 audin->listener_callback->plugin = pPlugin;
703 audin->listener_callback->channel_mgr = pChannelMgr;
704 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
705 &audin->listener_callback->iface, &audin->listener);
707 audin->initialized = rc == CHANNEL_RC_OK;
716 static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
718 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
719 UINT error = CHANNEL_RC_OK;
722 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
724 WLog_Print(audin->log, WLOG_TRACE,
"...");
726 if (audin->listener_callback)
728 IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
730 IFCALL(mgr->DestroyListener, mgr, audin->listener);
732 audio_formats_free(audin->fixed_format, 1);
736 IFCALLRET(audin->device->Free, error, audin->device);
738 if (error != CHANNEL_RC_OK)
740 WLog_Print(audin->log, WLOG_ERROR,
"Free failed with errorcode %" PRIu32
"", error);
744 audin->device = NULL;
747 freerdp_dsp_context_free(audin->dsp_context);
748 Stream_Free(audin->data, TRUE);
749 free(audin->subsystem);
750 free(audin->device_name);
751 free(audin->listener_callback);
753 return CHANNEL_RC_OK;
756 static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
758 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
759 UINT error = CHANNEL_RC_OK;
762 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
764 audin->attached = TRUE;
768 static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
770 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
771 UINT error = CHANNEL_RC_OK;
774 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
776 audin->attached = FALSE;
785 static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
787 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
793 WLog_Print(audin->log, WLOG_ERROR,
"existing device, abort.");
794 return ERROR_ALREADY_EXISTS;
797 WLog_Print(audin->log, WLOG_DEBUG,
"device registered.");
798 audin->device = device;
799 return CHANNEL_RC_OK;
807 static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin,
const char* name,
const ADDIN_ARGV* args)
812 UINT error = ERROR_INTERNAL_ERROR;
814 PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name, NULL, 0);
815 PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
819 WLog_Print(audin->log, WLOG_ERROR,
820 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
822 return ERROR_INVALID_FUNCTION;
825 entryPoints.plugin = &audin->iface;
826 entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
827 entryPoints.args = args;
828 entryPoints.rdpcontext = audin->rdpcontext;
830 error = entry(&entryPoints);
833 WLog_Print(audin->log, WLOG_ERROR,
"%s entry returned error %" PRIu32
".", name, error);
837 WLog_Print(audin->log, WLOG_INFO,
"Loaded %s backend for audin", name);
838 return CHANNEL_RC_OK;
846 static UINT audin_set_subsystem(AUDIN_PLUGIN* audin,
const char* subsystem)
850 free(audin->subsystem);
851 audin->subsystem = _strdup(subsystem);
853 if (!audin->subsystem)
855 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
856 return ERROR_NOT_ENOUGH_MEMORY;
859 return CHANNEL_RC_OK;
867 static UINT audin_set_device_name(AUDIN_PLUGIN* audin,
const char* device_name)
871 free(audin->device_name);
872 audin->device_name = _strdup(device_name);
874 if (!audin->device_name)
876 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
877 return ERROR_NOT_ENOUGH_MEMORY;
880 return CHANNEL_RC_OK;
883 BOOL audin_process_addin_args(AUDIN_PLUGIN* audin,
const ADDIN_ARGV* args)
886 {
"sys", COMMAND_LINE_VALUE_REQUIRED,
"<subsystem>", NULL, NULL, -1, NULL,
"subsystem" },
887 {
"dev", COMMAND_LINE_VALUE_REQUIRED,
"<device>", NULL, NULL, -1, NULL,
"device" },
888 {
"format", COMMAND_LINE_VALUE_REQUIRED,
"<format>", NULL, NULL, -1, NULL,
"format" },
889 {
"rate", COMMAND_LINE_VALUE_REQUIRED,
"<rate>", NULL, NULL, -1, NULL,
"rate" },
890 {
"channel", COMMAND_LINE_VALUE_REQUIRED,
"<channel>", NULL, NULL, -1, NULL,
"channel" },
891 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
894 if (!args || args->argc == 1)
898 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
900 CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin, NULL, NULL);
910 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
913 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"sys")
915 const UINT error = audin_set_subsystem(audin, arg->Value);
916 if (error != CHANNEL_RC_OK)
918 WLog_Print(audin->log, WLOG_ERROR,
919 "audin_set_subsystem failed with error %" PRIu32
"!", error);
923 CommandLineSwitchCase(arg,
"dev")
925 const UINT error = audin_set_device_name(audin, arg->Value);
926 if (error != CHANNEL_RC_OK)
928 WLog_Print(audin->log, WLOG_ERROR,
929 "audin_set_device_name failed with error %" PRIu32
"!", error);
933 CommandLineSwitchCase(arg,
"format")
935 unsigned long val = strtoul(arg->Value, NULL, 0);
937 if ((errno != 0) || (val > UINT16_MAX))
940 audin->fixed_format->wFormatTag = (UINT16)val;
942 CommandLineSwitchCase(arg,
"rate")
944 unsigned long val = strtoul(arg->Value, NULL, 0);
946 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
949 audin->fixed_format->nSamplesPerSec = (UINT32)val;
951 CommandLineSwitchCase(arg,
"channel")
953 unsigned long val = strtoul(arg->Value, NULL, 0);
955 if ((errno != 0) || (val <= UINT16_MAX))
956 audin->fixed_format->nChannels = (UINT16)val;
958 CommandLineSwitchDefault(arg)
961 CommandLineSwitchEnd(arg)
962 }
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
972 FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
974 struct SubsystemEntry
979 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
980 struct SubsystemEntry entries[] =
982 #if defined(WITH_PULSE)
985 #if defined(WITH_OSS)
986 {
"oss",
"default" },
988 #if defined(WITH_ALSA)
989 {
"alsa",
"default" },
991 #if defined(WITH_OPENSLES)
992 {
"opensles",
"default" },
994 #if defined(WITH_WINMM)
995 {
"winmm",
"default" },
997 #if defined(WITH_MACAUDIO)
998 {
"mac",
"default" },
1000 #if defined(WITH_IOSAUDIO)
1001 {
"ios",
"default" },
1003 #if defined(WITH_SNDIO)
1004 {
"sndio",
"default" },
1008 struct SubsystemEntry* entry = &entries[0];
1009 WINPR_ASSERT(pEntryPoints);
1010 WINPR_ASSERT(pEntryPoints->GetPlugin);
1011 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1014 return CHANNEL_RC_ALREADY_INITIALIZED;
1016 audin = (AUDIN_PLUGIN*)calloc(1,
sizeof(AUDIN_PLUGIN));
1020 WLog_ERR(TAG,
"calloc failed!");
1021 return CHANNEL_RC_NO_MEMORY;
1024 audin->log = WLog_Get(TAG);
1025 audin->data = Stream_New(NULL, 4096);
1026 audin->fixed_format = audio_format_new();
1028 if (!audin->fixed_format)
1034 audin->dsp_context = freerdp_dsp_context_new(TRUE);
1036 if (!audin->dsp_context)
1039 audin->attached = TRUE;
1040 audin->iface.Initialize = audin_plugin_initialize;
1041 audin->iface.Connected = NULL;
1042 audin->iface.Disconnected = NULL;
1043 audin->iface.Terminated = audin_plugin_terminated;
1044 audin->iface.Attached = audin_plugin_attached;
1045 audin->iface.Detached = audin_plugin_detached;
1047 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1048 audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1052 if (!audin_process_addin_args(audin, args))
1056 if (audin->subsystem)
1058 if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1061 audin->log, WLOG_ERROR,
1062 "Unable to load microphone redirection subsystem %s because of error %" PRIu32
"",
1063 audin->subsystem, error);
1069 while (entry && entry->subsystem && !audin->device)
1071 if ((error = audin_set_subsystem(audin, entry->subsystem)))
1073 WLog_Print(audin->log, WLOG_ERROR,
1074 "audin_set_subsystem for %s failed with error %" PRIu32
"!",
1075 entry->subsystem, error);
1077 else if ((error = audin_set_device_name(audin, entry->device)))
1079 WLog_Print(audin->log, WLOG_ERROR,
1080 "audin_set_device_name for %s failed with error %" PRIu32
"!",
1081 entry->subsystem, error);
1083 else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1085 WLog_Print(audin->log, WLOG_ERROR,
1086 "audin_load_device_plugin %s failed with error %" PRIu32
"!",
1087 entry->subsystem, error);
1094 if (audin->device == NULL)
1098 WLog_Print(audin->log, WLOG_ERROR,
"No microphone device could be found.");
1099 error = CHANNEL_RC_OK;
1103 error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1104 if (error == CHANNEL_RC_OK)
1108 audin_plugin_terminated(&audin->iface);