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);
491 if (initialFormat >= callback->formats_count)
493 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
494 initialFormat, callback->formats_count);
495 return ERROR_INVALID_DATA;
500 if (FramesPerPacket >= INT32_MAX)
502 WLog_Print(audin->log, WLOG_ERROR,
"invalid frames per packet %" PRIu32, FramesPerPacket);
503 return ERROR_INVALID_DATA;
506 audin->FramesPerPacket = FramesPerPacket;
507 audin->format = &callback->formats[initialFormat];
509 if (!audin_open_device(audin, callback))
510 return ERROR_INTERNAL_ERROR;
512 if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
514 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_format_change_pdu failed!");
518 if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
519 WLog_Print(audin->log, WLOG_ERROR,
"audin_send_open_reply_pdu failed!");
529static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
532 UINT32 NewFormat = 0;
533 UINT error = CHANNEL_RC_OK;
535 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
536 return ERROR_INVALID_DATA;
538 Stream_Read_UINT32(s, NewFormat);
539 WLog_Print(audin->log, WLOG_DEBUG,
"NewFormat=%" PRIu32
"", NewFormat);
541 if (NewFormat >= callback->formats_count)
543 WLog_Print(audin->log, WLOG_ERROR,
"invalid format index %" PRIu32
" (total %" PRIu32
")",
544 NewFormat, callback->formats_count);
545 return ERROR_INVALID_DATA;
548 audin->format = &callback->formats[NewFormat];
552 IFCALLRET(audin->device->Close, error, audin->device);
554 if (error != CHANNEL_RC_OK)
556 WLog_ERR(TAG,
"Close failed with errorcode %" PRIu32
"", error);
561 if (!audin_open_device(audin, callback))
562 return ERROR_INTERNAL_ERROR;
564 if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
565 WLog_ERR(TAG,
"audin_send_format_change_pdu failed!");
575static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
wStream* data)
579 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
581 if (!callback || !data)
582 return ERROR_INVALID_PARAMETER;
584 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
587 return ERROR_INTERNAL_ERROR;
589 if (!Stream_CheckAndLogRequiredLength(TAG, data, 1))
590 return ERROR_NO_DATA;
592 Stream_Read_UINT8(data, MessageId);
593 WLog_Print(audin->log, WLOG_DEBUG,
"MessageId=0x%02" PRIx8
"", MessageId);
597 case MSG_SNDIN_VERSION:
598 error = audin_process_version(audin, callback, data);
601 case MSG_SNDIN_FORMATS:
602 error = audin_process_formats(audin, callback, data);
606 error = audin_process_open(audin, callback, data);
609 case MSG_SNDIN_FORMATCHANGE:
610 error = audin_process_format_change(audin, callback, data);
614 WLog_Print(audin->log, WLOG_ERROR,
"unknown MessageId=0x%02" PRIx8
"", MessageId);
615 error = ERROR_INVALID_DATA;
627static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
629 AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
630 WINPR_ASSERT(callback);
632 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
635 UINT error = CHANNEL_RC_OK;
636 WLog_Print(audin->log, WLOG_TRACE,
"...");
640 IFCALLRET(audin->device->Close, error, audin->device);
642 if (error != CHANNEL_RC_OK)
643 WLog_Print(audin->log, WLOG_ERROR,
"Close failed with errorcode %" PRIu32
"", error);
646 audin->format =
nullptr;
647 audio_formats_free(callback->formats, callback->formats_count);
657static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
658 IWTSVirtualChannel* pChannel,
659 WINPR_ATTR_UNUSED BYTE* Data,
660 WINPR_ATTR_UNUSED BOOL* pbAccept,
661 IWTSVirtualChannelCallback** ppCallback)
665 if (!listener_callback || !listener_callback->plugin)
666 return ERROR_INTERNAL_ERROR;
668 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
669 WLog_Print(audin->log, WLOG_TRACE,
"...");
670 AUDIN_CHANNEL_CALLBACK* callback =
671 (AUDIN_CHANNEL_CALLBACK*)calloc(1,
sizeof(AUDIN_CHANNEL_CALLBACK));
675 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
676 return CHANNEL_RC_NO_MEMORY;
679 callback->iface.OnDataReceived = audin_on_data_received;
680 callback->iface.OnClose = audin_on_close;
681 callback->plugin = listener_callback->plugin;
682 callback->channel_mgr = listener_callback->channel_mgr;
683 callback->channel = pChannel;
684 *ppCallback = &callback->iface;
685 return CHANNEL_RC_OK;
693static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
695 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
698 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
701 return ERROR_INVALID_PARAMETER;
703 if (audin->initialized)
705 WLog_ERR(TAG,
"[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
706 return ERROR_INVALID_DATA;
709 WLog_Print(audin->log, WLOG_TRACE,
"...");
710 audin->listener_callback =
713 if (!audin->listener_callback)
715 WLog_Print(audin->log, WLOG_ERROR,
"calloc failed!");
716 return CHANNEL_RC_NO_MEMORY;
719 audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
720 audin->listener_callback->plugin = pPlugin;
721 audin->listener_callback->channel_mgr = pChannelMgr;
722 const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
723 &audin->listener_callback->iface, &audin->listener);
725 audin->initialized = rc == CHANNEL_RC_OK;
734static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
736 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
737 UINT error = CHANNEL_RC_OK;
740 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
742 WLog_Print(audin->log, WLOG_TRACE,
"...");
744 if (audin->listener_callback)
746 IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
748 IFCALL(mgr->DestroyListener, mgr, audin->listener);
750 audio_formats_free(audin->fixed_format, 1);
754 IFCALLRET(audin->device->Free, error, audin->device);
756 if (error != CHANNEL_RC_OK)
758 WLog_Print(audin->log, WLOG_ERROR,
"Free failed with errorcode %" PRIu32
"", error);
762 audin->device =
nullptr;
765 freerdp_dsp_context_free(audin->dsp_context);
766 Stream_Free(audin->data, TRUE);
767 free(audin->subsystem);
768 free(audin->device_name);
769 free(audin->listener_callback);
771 return CHANNEL_RC_OK;
774static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
776 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
777 UINT error = CHANNEL_RC_OK;
780 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
782 audin->attached = TRUE;
786static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
788 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
789 UINT error = CHANNEL_RC_OK;
792 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
794 audin->attached = FALSE;
803static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
805 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
811 WLog_Print(audin->log, WLOG_ERROR,
"existing device, abort.");
812 return ERROR_ALREADY_EXISTS;
815 WLog_Print(audin->log, WLOG_DEBUG,
"device registered.");
816 audin->device = device;
817 return CHANNEL_RC_OK;
825static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin,
const char* name,
const ADDIN_ARGV* args)
830 UINT error = ERROR_INTERNAL_ERROR;
832 PVIRTUALCHANNELENTRY pvce =
833 freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME, name,
nullptr, 0);
834 PFREERDP_AUDIN_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_AUDIN_DEVICE_ENTRY);
836 if (entry ==
nullptr)
838 WLog_Print(audin->log, WLOG_ERROR,
839 "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
841 return ERROR_INVALID_FUNCTION;
844 entryPoints.plugin = &audin->iface;
845 entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
846 entryPoints.args = args;
847 entryPoints.rdpcontext = audin->rdpcontext;
849 error = entry(&entryPoints);
852 WLog_Print(audin->log, WLOG_ERROR,
"%s entry returned error %" PRIu32
".", name, error);
856 WLog_Print(audin->log, WLOG_INFO,
"Loaded %s backend for audin", name);
857 return CHANNEL_RC_OK;
865static UINT audin_set_subsystem(AUDIN_PLUGIN* audin,
const char* subsystem)
869 free(audin->subsystem);
870 audin->subsystem = _strdup(subsystem);
872 if (!audin->subsystem)
874 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
875 return ERROR_NOT_ENOUGH_MEMORY;
878 return CHANNEL_RC_OK;
886static UINT audin_set_device_name(AUDIN_PLUGIN* audin,
const char* device_name)
890 free(audin->device_name);
891 audin->device_name = _strdup(device_name);
893 if (!audin->device_name)
895 WLog_Print(audin->log, WLOG_ERROR,
"_strdup failed!");
896 return ERROR_NOT_ENOUGH_MEMORY;
899 return CHANNEL_RC_OK;
902BOOL audin_process_addin_args(AUDIN_PLUGIN* audin,
const ADDIN_ARGV* args)
905 {
"sys", COMMAND_LINE_VALUE_REQUIRED,
"<subsystem>",
nullptr,
nullptr, -1,
nullptr,
907 {
"dev", COMMAND_LINE_VALUE_REQUIRED,
"<device>",
nullptr,
nullptr, -1,
nullptr,
"device" },
908 {
"format", COMMAND_LINE_VALUE_REQUIRED,
"<format>",
nullptr,
nullptr, -1,
nullptr,
910 {
"rate", COMMAND_LINE_VALUE_REQUIRED,
"<rate>",
nullptr,
nullptr, -1,
nullptr,
"rate" },
911 {
"channel", COMMAND_LINE_VALUE_REQUIRED,
"<channel>",
nullptr,
nullptr, -1,
nullptr,
913 {
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr }
916 if (!args || args->argc == 1)
920 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
921 const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin,
932 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
935 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"sys")
937 const UINT error = audin_set_subsystem(audin, arg->Value);
938 if (error != CHANNEL_RC_OK)
940 WLog_Print(audin->log, WLOG_ERROR,
941 "audin_set_subsystem failed with error %" PRIu32
"!", error);
945 CommandLineSwitchCase(arg,
"dev")
947 const UINT error = audin_set_device_name(audin, arg->Value);
948 if (error != CHANNEL_RC_OK)
950 WLog_Print(audin->log, WLOG_ERROR,
951 "audin_set_device_name failed with error %" PRIu32
"!", error);
955 CommandLineSwitchCase(arg,
"format")
957 unsigned long val = strtoul(arg->Value,
nullptr, 0);
959 if ((errno != 0) || (val > UINT16_MAX))
962 audin->fixed_format->wFormatTag = (UINT16)val;
964 CommandLineSwitchCase(arg,
"rate")
966 unsigned long val = strtoul(arg->Value,
nullptr, 0);
968 if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
971 audin->fixed_format->nSamplesPerSec = (UINT32)val;
973 CommandLineSwitchCase(arg,
"channel")
975 unsigned long val = strtoul(arg->Value,
nullptr, 0);
977 if ((errno != 0) || (val <= UINT16_MAX))
978 audin->fixed_format->nChannels = (UINT16)val;
980 CommandLineSwitchDefault(arg)
983 CommandLineSwitchEnd(arg)
984 }
while ((arg = CommandLineFindNextArgumentA(arg)) !=
nullptr);
994FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
996 struct SubsystemEntry
1001 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
1002 struct SubsystemEntry entries[] = {
1003#if defined(WITH_PULSE)
1006#if defined(WITH_OSS)
1007 {
"oss",
"default" },
1009#if defined(WITH_ALSA)
1010 {
"alsa",
"default" },
1012#if defined(WITH_OPENSLES)
1013 {
"opensles",
"default" },
1015#if defined(WITH_WINMM)
1016 {
"winmm",
"default" },
1018#if defined(WITH_MACAUDIO)
1019 {
"mac",
"default" },
1021#if defined(WITH_IOSAUDIO)
1022 {
"ios",
"default" },
1024#if defined(WITH_SNDIO)
1025 {
"sndio",
"default" },
1027 {
nullptr,
nullptr }
1029 struct SubsystemEntry* entry = &entries[0];
1030 WINPR_ASSERT(pEntryPoints);
1031 WINPR_ASSERT(pEntryPoints->GetPlugin);
1032 AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1034 if (audin !=
nullptr)
1035 return CHANNEL_RC_ALREADY_INITIALIZED;
1037 audin = (AUDIN_PLUGIN*)calloc(1,
sizeof(AUDIN_PLUGIN));
1041 WLog_ERR(TAG,
"calloc failed!");
1042 return CHANNEL_RC_NO_MEMORY;
1045 audin->log = WLog_Get(TAG);
1046 audin->data = Stream_New(
nullptr, 4096);
1047 audin->fixed_format = audio_format_new();
1049 if (!audin->fixed_format)
1055 audin->dsp_context = freerdp_dsp_context_new(TRUE);
1057 if (!audin->dsp_context)
1060 audin->attached = TRUE;
1061 audin->iface.Initialize = audin_plugin_initialize;
1062 audin->iface.Connected =
nullptr;
1063 audin->iface.Disconnected =
nullptr;
1064 audin->iface.Terminated = audin_plugin_terminated;
1065 audin->iface.Attached = audin_plugin_attached;
1066 audin->iface.Detached = audin_plugin_detached;
1069 const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1070 audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1074 if (!audin_process_addin_args(audin, args))
1078 if (audin->subsystem)
1080 if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1083 audin->log, WLOG_ERROR,
1084 "Unable to load microphone redirection subsystem %s because of error %" PRIu32
1086 audin->subsystem, error);
1092 while (entry && entry->subsystem && !audin->device)
1094 if ((error = audin_set_subsystem(audin, entry->subsystem)))
1096 WLog_Print(audin->log, WLOG_ERROR,
1097 "audin_set_subsystem for %s failed with error %" PRIu32
"!",
1098 entry->subsystem, error);
1100 else if ((error = audin_set_device_name(audin, entry->device)))
1102 WLog_Print(audin->log, WLOG_ERROR,
1103 "audin_set_device_name for %s failed with error %" PRIu32
"!",
1104 entry->subsystem, error);
1106 else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1108 WLog_Print(audin->log, WLOG_ERROR,
1109 "audin_load_device_plugin %s failed with error %" PRIu32
"!",
1110 entry->subsystem, error);
1118 if (audin->device ==
nullptr)
1122 WLog_Print(audin->log, WLOG_ERROR,
"No microphone device could be found.");
1123 error = CHANNEL_RC_OK;
1127 error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1128 if (error == CHANNEL_RC_OK)
1132 audin_plugin_terminated(&audin->iface);