FreeRDP
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 
47 typedef 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 
58 typedef 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 
75 typedef 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 
104 static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args);
105 
106 static 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 
131 static 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 
172 static 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 
187 static 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);
279 out:
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 
296 static 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 
320 static 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 
344 static 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 
401 static 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 
469 static 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 
513 static 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 
559 static 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 
611 static 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 
641 static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
642  IWTSVirtualChannel* pChannel, BYTE* Data,
643  BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
644 {
645  GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
646 
647  if (!listener_callback || !listener_callback->plugin)
648  return ERROR_INTERNAL_ERROR;
649 
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));
654 
655  if (!callback)
656  {
657  WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
658  return CHANNEL_RC_NO_MEMORY;
659  }
660 
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;
668 }
669 
675 static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
676 {
677  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
678 
679  if (!audin)
680  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
681 
682  if (!pChannelMgr)
683  return ERROR_INVALID_PARAMETER;
684 
685  if (audin->initialized)
686  {
687  WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
688  return ERROR_INVALID_DATA;
689  }
690 
691  WLog_Print(audin->log, WLOG_TRACE, "...");
692  audin->listener_callback =
694 
695  if (!audin->listener_callback)
696  {
697  WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
698  return CHANNEL_RC_NO_MEMORY;
699  }
700 
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);
706 
707  audin->initialized = rc == CHANNEL_RC_OK;
708  return rc;
709 }
710 
716 static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
717 {
718  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
719  UINT error = CHANNEL_RC_OK;
720 
721  if (!audin)
722  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
723 
724  WLog_Print(audin->log, WLOG_TRACE, "...");
725 
726  if (audin->listener_callback)
727  {
728  IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
729  if (mgr)
730  IFCALL(mgr->DestroyListener, mgr, audin->listener);
731  }
732  audio_formats_free(audin->fixed_format, 1);
733 
734  if (audin->device)
735  {
736  IFCALLRET(audin->device->Free, error, audin->device);
737 
738  if (error != CHANNEL_RC_OK)
739  {
740  WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %" PRIu32 "", error);
741  // dont stop on error
742  }
743 
744  audin->device = NULL;
745  }
746 
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);
752  free(audin);
753  return CHANNEL_RC_OK;
754 }
755 
756 static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
757 {
758  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
759  UINT error = CHANNEL_RC_OK;
760 
761  if (!audin)
762  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
763 
764  audin->attached = TRUE;
765  return error;
766 }
767 
768 static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
769 {
770  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
771  UINT error = CHANNEL_RC_OK;
772 
773  if (!audin)
774  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
775 
776  audin->attached = FALSE;
777  return error;
778 }
779 
785 static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
786 {
787  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
788 
789  WINPR_ASSERT(audin);
790 
791  if (audin->device)
792  {
793  WLog_Print(audin->log, WLOG_ERROR, "existing device, abort.");
794  return ERROR_ALREADY_EXISTS;
795  }
796 
797  WLog_Print(audin->log, WLOG_DEBUG, "device registered.");
798  audin->device = device;
799  return CHANNEL_RC_OK;
800 }
801 
807 static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, const ADDIN_ARGV* args)
808 {
809  WINPR_ASSERT(audin);
810 
811  FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints = { 0 };
812  UINT error = ERROR_INTERNAL_ERROR;
813 
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);
816 
817  if (entry == NULL)
818  {
819  WLog_Print(audin->log, WLOG_ERROR,
820  "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
821  name);
822  return ERROR_INVALID_FUNCTION;
823  }
824 
825  entryPoints.plugin = &audin->iface;
826  entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
827  entryPoints.args = args;
828  entryPoints.rdpcontext = audin->rdpcontext;
829 
830  error = entry(&entryPoints);
831  if (error)
832  {
833  WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
834  return error;
835  }
836 
837  WLog_Print(audin->log, WLOG_INFO, "Loaded %s backend for audin", name);
838  return CHANNEL_RC_OK;
839 }
840 
846 static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
847 {
848  WINPR_ASSERT(audin);
849 
850  free(audin->subsystem);
851  audin->subsystem = _strdup(subsystem);
852 
853  if (!audin->subsystem)
854  {
855  WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
856  return ERROR_NOT_ENOUGH_MEMORY;
857  }
858 
859  return CHANNEL_RC_OK;
860 }
861 
867 static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
868 {
869  WINPR_ASSERT(audin);
870 
871  free(audin->device_name);
872  audin->device_name = _strdup(device_name);
873 
874  if (!audin->device_name)
875  {
876  WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
877  return ERROR_NOT_ENOUGH_MEMORY;
878  }
879 
880  return CHANNEL_RC_OK;
881 }
882 
883 BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
884 {
885  COMMAND_LINE_ARGUMENT_A audin_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 }
892  };
893 
894  if (!args || args->argc == 1)
895  return TRUE;
896 
897  const DWORD flags =
898  COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
899  const int status =
900  CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin, NULL, NULL);
901 
902  if (status != 0)
903  return FALSE;
904 
905  const COMMAND_LINE_ARGUMENT_A* arg = audin_args;
906  errno = 0;
907 
908  do
909  {
910  if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
911  continue;
912 
913  CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
914  {
915  const UINT error = audin_set_subsystem(audin, arg->Value);
916  if (error != CHANNEL_RC_OK)
917  {
918  WLog_Print(audin->log, WLOG_ERROR,
919  "audin_set_subsystem failed with error %" PRIu32 "!", error);
920  return FALSE;
921  }
922  }
923  CommandLineSwitchCase(arg, "dev")
924  {
925  const UINT error = audin_set_device_name(audin, arg->Value);
926  if (error != CHANNEL_RC_OK)
927  {
928  WLog_Print(audin->log, WLOG_ERROR,
929  "audin_set_device_name failed with error %" PRIu32 "!", error);
930  return FALSE;
931  }
932  }
933  CommandLineSwitchCase(arg, "format")
934  {
935  unsigned long val = strtoul(arg->Value, NULL, 0);
936 
937  if ((errno != 0) || (val > UINT16_MAX))
938  return FALSE;
939 
940  audin->fixed_format->wFormatTag = (UINT16)val;
941  }
942  CommandLineSwitchCase(arg, "rate")
943  {
944  unsigned long val = strtoul(arg->Value, NULL, 0);
945 
946  if ((errno != 0) || (val == 0) || (val > UINT32_MAX))
947  return FALSE;
948 
949  audin->fixed_format->nSamplesPerSec = (UINT32)val;
950  }
951  CommandLineSwitchCase(arg, "channel")
952  {
953  unsigned long val = strtoul(arg->Value, NULL, 0);
954 
955  if ((errno != 0) || (val <= UINT16_MAX))
956  audin->fixed_format->nChannels = (UINT16)val;
957  }
958  CommandLineSwitchDefault(arg)
959  {
960  }
961  CommandLineSwitchEnd(arg)
962  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
963 
964  return TRUE;
965 }
966 
972 FREERDP_ENTRY_POINT(UINT VCAPITYPE audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
973 {
974  struct SubsystemEntry
975  {
976  char* subsystem;
977  char* device;
978  };
979  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
980  struct SubsystemEntry entries[] =
981  {
982 #if defined(WITH_PULSE)
983  { "pulse", "" },
984 #endif
985 #if defined(WITH_OSS)
986  { "oss", "default" },
987 #endif
988 #if defined(WITH_ALSA)
989  { "alsa", "default" },
990 #endif
991 #if defined(WITH_OPENSLES)
992  { "opensles", "default" },
993 #endif
994 #if defined(WITH_WINMM)
995  { "winmm", "default" },
996 #endif
997 #if defined(WITH_MACAUDIO)
998  { "mac", "default" },
999 #endif
1000 #if defined(WITH_IOSAUDIO)
1001  { "ios", "default" },
1002 #endif
1003 #if defined(WITH_SNDIO)
1004  { "sndio", "default" },
1005 #endif
1006  { NULL, NULL }
1007  };
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);
1012 
1013  if (audin != NULL)
1014  return CHANNEL_RC_ALREADY_INITIALIZED;
1015 
1016  audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
1017 
1018  if (!audin)
1019  {
1020  WLog_ERR(TAG, "calloc failed!");
1021  return CHANNEL_RC_NO_MEMORY;
1022  }
1023 
1024  audin->log = WLog_Get(TAG);
1025  audin->data = Stream_New(NULL, 4096);
1026  audin->fixed_format = audio_format_new();
1027 
1028  if (!audin->fixed_format)
1029  goto out;
1030 
1031  if (!audin->data)
1032  goto out;
1033 
1034  audin->dsp_context = freerdp_dsp_context_new(TRUE);
1035 
1036  if (!audin->dsp_context)
1037  goto out;
1038 
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;
1046 
1047  const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1048  audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1049 
1050  if (args)
1051  {
1052  if (!audin_process_addin_args(audin, args))
1053  goto out;
1054  }
1055 
1056  if (audin->subsystem)
1057  {
1058  if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1059  {
1060  WLog_Print(
1061  audin->log, WLOG_ERROR,
1062  "Unable to load microphone redirection subsystem %s because of error %" PRIu32 "",
1063  audin->subsystem, error);
1064  goto out;
1065  }
1066  }
1067  else
1068  {
1069  while (entry && entry->subsystem && !audin->device)
1070  {
1071  if ((error = audin_set_subsystem(audin, entry->subsystem)))
1072  {
1073  WLog_Print(audin->log, WLOG_ERROR,
1074  "audin_set_subsystem for %s failed with error %" PRIu32 "!",
1075  entry->subsystem, error);
1076  }
1077  else if ((error = audin_set_device_name(audin, entry->device)))
1078  {
1079  WLog_Print(audin->log, WLOG_ERROR,
1080  "audin_set_device_name for %s failed with error %" PRIu32 "!",
1081  entry->subsystem, error);
1082  }
1083  else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1084  {
1085  WLog_Print(audin->log, WLOG_ERROR,
1086  "audin_load_device_plugin %s failed with error %" PRIu32 "!",
1087  entry->subsystem, error);
1088  }
1089 
1090  entry++;
1091  }
1092  }
1093 
1094  if (audin->device == NULL)
1095  {
1096  /* If we have no audin device do not register plugin but still return OK or the client will
1097  * just disconnect due to a missing microphone. */
1098  WLog_Print(audin->log, WLOG_ERROR, "No microphone device could be found.");
1099  error = CHANNEL_RC_OK;
1100  goto out;
1101  }
1102 
1103  error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1104  if (error == CHANNEL_RC_OK)
1105  return error;
1106 
1107 out:
1108  audin_plugin_terminated(&audin->iface);
1109  return error;
1110 }
Definition: client/audin.h:59