Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/channels/audin/client/audin_main.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Audio Input Redirection Virtual Channel
4
 *
5
 * Copyright 2010-2011 Vic Lee
6
 * Copyright 2015 Thincast Technologies GmbH
7
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8
 * Copyright 2015 Armin Novak <armin.novak@thincast.com>
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
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
0
#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
66
  /**
67
   * The supported format list sent back to the server, which needs to
68
   * be stored as reference when the server sends the format index in
69
   * Open PDU and Format Change PDU
70
   */
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
0
{
109
0
  if (!callback || !out)
110
0
    return ERROR_INVALID_PARAMETER;
111
112
0
  if (!callback->channel || !callback->channel->Write)
113
0
    return ERROR_INTERNAL_ERROR;
114
115
0
  Stream_SealLength(out);
116
0
  WINPR_ASSERT(Stream_Length(out) <= ULONG_MAX);
117
0
  const UINT error = callback->channel->Write(callback->channel, (ULONG)Stream_Length(out),
118
0
                                              Stream_Buffer(out), NULL);
119
120
0
  if (freeStream)
121
0
    Stream_Free(out, TRUE);
122
123
0
  return error;
124
0
}
125
126
/**
127
 * Function description
128
 *
129
 * @return 0 on success, otherwise a Win32 error code
130
 */
131
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
132
0
{
133
0
  const UINT32 ClientVersion = SNDIN_VERSION;
134
0
  UINT32 ServerVersion = 0;
135
136
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
137
0
    return ERROR_INVALID_DATA;
138
139
0
  Stream_Read_UINT32(s, ServerVersion);
140
0
  WLog_Print(audin->log, WLOG_DEBUG, "ServerVersion=%" PRIu32 ", ClientVersion=%" PRIu32,
141
0
             ServerVersion, ClientVersion);
142
143
  /* Do not answer server packet, we do not support the channel version. */
144
0
  if (ServerVersion > ClientVersion)
145
0
  {
146
0
    WLog_Print(audin->log, WLOG_WARN,
147
0
               "Incompatible channel version server=%" PRIu32
148
0
               ", client supports version=%" PRIu32,
149
0
               ServerVersion, ClientVersion);
150
0
    return CHANNEL_RC_OK;
151
0
  }
152
0
  audin->version = ServerVersion;
153
154
0
  wStream* out = Stream_New(NULL, 5);
155
156
0
  if (!out)
157
0
  {
158
0
    WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
159
0
    return ERROR_OUTOFMEMORY;
160
0
  }
161
162
0
  Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
163
0
  Stream_Write_UINT32(out, ClientVersion);
164
0
  return audin_channel_write_and_free(callback, out, TRUE);
165
0
}
166
167
/**
168
 * Function description
169
 *
170
 * @return 0 on success, otherwise a Win32 error code
171
 */
172
static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
173
0
{
174
0
  BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
175
176
0
  if (!callback || !callback->channel || !callback->channel->Write)
177
0
    return ERROR_INTERNAL_ERROR;
178
179
0
  return callback->channel->Write(callback->channel, 1, out_data, NULL);
180
0
}
181
182
/**
183
 * Function description
184
 *
185
 * @return 0 on success, otherwise a Win32 error code
186
 */
187
static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
188
0
{
189
0
  UINT error = ERROR_INTERNAL_ERROR;
190
0
  UINT32 NumFormats = 0;
191
0
  UINT32 cbSizeFormatsPacket = 0;
192
193
0
  WINPR_ASSERT(audin);
194
0
  WINPR_ASSERT(callback);
195
196
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
197
0
    return ERROR_INVALID_DATA;
198
199
0
  Stream_Read_UINT32(s, NumFormats);
200
0
  WLog_Print(audin->log, WLOG_DEBUG, "NumFormats %" PRIu32 "", NumFormats);
201
202
0
  if ((NumFormats < 1) || (NumFormats > 1000))
203
0
  {
204
0
    WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %" PRIu32 "", NumFormats);
205
0
    return ERROR_INVALID_DATA;
206
0
  }
207
208
0
  Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
209
0
  callback->formats = audio_formats_new(NumFormats);
210
211
0
  if (!callback->formats)
212
0
  {
213
0
    WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
214
0
    return ERROR_INVALID_DATA;
215
0
  }
216
217
0
  wStream* out = Stream_New(NULL, 9);
218
219
0
  if (!out)
220
0
  {
221
0
    error = CHANNEL_RC_NO_MEMORY;
222
0
    WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
223
0
    goto out;
224
0
  }
225
226
0
  Stream_Seek(out, 9);
227
228
  /* SoundFormats (variable) */
229
0
  for (UINT32 i = 0; i < NumFormats; i++)
230
0
  {
231
0
    AUDIO_FORMAT format = { 0 };
232
233
0
    if (!audio_format_read(s, &format))
234
0
    {
235
0
      error = ERROR_INVALID_DATA;
236
0
      goto out;
237
0
    }
238
239
0
    audio_format_print(audin->log, WLOG_DEBUG, &format);
240
241
0
    if (!audio_format_compatible(audin->fixed_format, &format))
242
0
    {
243
0
      audio_format_free(&format);
244
0
      continue;
245
0
    }
246
247
0
    if (freerdp_dsp_supports_format(&format, TRUE) ||
248
0
        audin->device->FormatSupported(audin->device, &format))
249
0
    {
250
      /* Store the agreed format in the corresponding index */
251
0
      callback->formats[callback->formats_count++] = format;
252
253
0
      if (!audio_format_write(out, &format))
254
0
      {
255
0
        error = CHANNEL_RC_NO_MEMORY;
256
0
        WLog_Print(audin->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
257
0
        goto out;
258
0
      }
259
0
    }
260
0
    else
261
0
    {
262
0
      audio_format_free(&format);
263
0
    }
264
0
  }
265
266
0
  if ((error = audin_send_incoming_data_pdu(callback)))
267
0
  {
268
0
    WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
269
0
    goto out;
270
0
  }
271
272
0
  cbSizeFormatsPacket = (UINT32)Stream_GetPosition(out);
273
0
  Stream_SetPosition(out, 0);
274
0
  Stream_Write_UINT8(out, MSG_SNDIN_FORMATS);        /* Header (1 byte) */
275
0
  Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
276
0
  Stream_Write_UINT32(out, cbSizeFormatsPacket);     /* cbSizeFormatsPacket (4 bytes) */
277
0
  Stream_SetPosition(out, cbSizeFormatsPacket);
278
0
  error = audin_channel_write_and_free(callback, out, FALSE);
279
0
out:
280
281
0
  if (error != CHANNEL_RC_OK)
282
0
  {
283
0
    audio_formats_free(callback->formats, NumFormats);
284
0
    callback->formats = NULL;
285
0
  }
286
287
0
  Stream_Free(out, TRUE);
288
0
  return error;
289
0
}
290
291
/**
292
 * Function description
293
 *
294
 * @return 0 on success, otherwise a Win32 error code
295
 */
296
static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
297
                                         UINT32 NewFormat)
298
0
{
299
0
  WINPR_ASSERT(audin);
300
0
  WINPR_ASSERT(callback);
301
302
0
  wStream* out = Stream_New(NULL, 5);
303
304
0
  if (!out)
305
0
  {
306
0
    WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
307
0
    return CHANNEL_RC_OK;
308
0
  }
309
310
0
  Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
311
0
  Stream_Write_UINT32(out, NewFormat);
312
0
  return audin_channel_write_and_free(callback, out, TRUE);
313
0
}
314
315
/**
316
 * Function description
317
 *
318
 * @return 0 on success, otherwise a Win32 error code
319
 */
320
static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
321
                                      UINT32 Result)
322
0
{
323
0
  WINPR_ASSERT(audin);
324
0
  WINPR_ASSERT(callback);
325
326
0
  wStream* out = Stream_New(NULL, 5);
327
328
0
  if (!out)
329
0
  {
330
0
    WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
331
0
    return CHANNEL_RC_NO_MEMORY;
332
0
  }
333
334
0
  Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
335
0
  Stream_Write_UINT32(out, Result);
336
0
  return audin_channel_write_and_free(callback, out, TRUE);
337
0
}
338
339
/**
340
 * Function description
341
 *
342
 * @return 0 on success, otherwise a Win32 error code
343
 */
344
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format, const BYTE* data, size_t size,
345
                                    void* user_data)
346
0
{
347
0
  WINPR_ASSERT(format);
348
349
0
  UINT error = ERROR_INTERNAL_ERROR;
350
0
  AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)user_data;
351
352
0
  if (!callback)
353
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
354
355
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
356
357
0
  if (!audin)
358
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
359
360
0
  if (!audin->attached)
361
0
    return CHANNEL_RC_OK;
362
363
0
  Stream_SetPosition(audin->data, 0);
364
365
0
  if (!Stream_EnsureRemainingCapacity(audin->data, 1))
366
0
    return CHANNEL_RC_NO_MEMORY;
367
368
0
  Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
369
370
0
  const BOOL compatible = audio_format_compatible(format, audin->format);
371
0
  if (compatible && audin->device->FormatSupported(audin->device, audin->format))
372
0
  {
373
0
    if (!Stream_EnsureRemainingCapacity(audin->data, size))
374
0
      return CHANNEL_RC_NO_MEMORY;
375
376
0
    Stream_Write(audin->data, data, size);
377
0
  }
378
0
  else
379
0
  {
380
0
    if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
381
0
      return ERROR_INTERNAL_ERROR;
382
0
  }
383
384
  /* Did not encode anything, skip this, the codec is not ready for output. */
385
0
  if (Stream_GetPosition(audin->data) <= 1)
386
0
    return CHANNEL_RC_OK;
387
388
0
  audio_format_print(audin->log, WLOG_TRACE, audin->format);
389
0
  WLog_Print(audin->log, WLOG_TRACE, "[%" PRIdz "/%" PRIdz "]", size,
390
0
             Stream_GetPosition(audin->data) - 1);
391
392
0
  if ((error = audin_send_incoming_data_pdu(callback)))
393
0
  {
394
0
    WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
395
0
    return error;
396
0
  }
397
398
0
  return audin_channel_write_and_free(callback, audin->data, FALSE);
399
0
}
400
401
static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
402
0
{
403
0
  UINT error = ERROR_INTERNAL_ERROR;
404
0
  AUDIO_FORMAT format = { 0 };
405
406
0
  if (!audin || !audin->device)
407
0
    return FALSE;
408
409
0
  format = *audin->format;
410
0
  const BOOL supported =
411
0
      IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
412
0
  WLog_Print(audin->log, WLOG_DEBUG, "microphone uses %s codec",
413
0
             audio_format_get_tag_string(format.wFormatTag));
414
415
0
  if (!supported)
416
0
  {
417
    /* Default sample rates supported by most backends. */
418
0
    const UINT32 samplerates[] = { format.nSamplesPerSec, 96000, 48000, 44100, 22050 };
419
0
    BOOL test = FALSE;
420
421
0
    format.wFormatTag = WAVE_FORMAT_PCM;
422
0
    format.wBitsPerSample = 16;
423
0
    format.cbSize = 0;
424
0
    for (size_t x = 0; x < ARRAYSIZE(samplerates); x++)
425
0
    {
426
0
      format.nSamplesPerSec = samplerates[x];
427
0
      for (UINT16 y = audin->format->nChannels; y > 0; y--)
428
0
      {
429
0
        format.nChannels = y;
430
0
        format.nBlockAlign = 2 * format.nChannels;
431
0
        test = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
432
0
        if (test)
433
0
          break;
434
0
      }
435
0
      if (test)
436
0
        break;
437
0
    }
438
0
    if (!test)
439
0
      return FALSE;
440
0
  }
441
442
0
  IFCALLRET(audin->device->SetFormat, error, audin->device, &format, audin->FramesPerPacket);
443
444
0
  if (error != CHANNEL_RC_OK)
445
0
  {
446
0
    WLog_ERR(TAG, "SetFormat failed with errorcode %" PRIu32 "", error);
447
0
    return FALSE;
448
0
  }
449
450
0
  if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format, audin->FramesPerPacket))
451
0
    return FALSE;
452
453
0
  IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
454
455
0
  if (error != CHANNEL_RC_OK)
456
0
  {
457
0
    WLog_ERR(TAG, "Open failed with errorcode %" PRIu32 "", error);
458
0
    return FALSE;
459
0
  }
460
461
0
  return TRUE;
462
0
}
463
464
/**
465
 * Function description
466
 *
467
 * @return 0 on success, otherwise a Win32 error code
468
 */
469
static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
470
0
{
471
0
  UINT32 initialFormat = 0;
472
0
  UINT32 FramesPerPacket = 0;
473
0
  UINT error = CHANNEL_RC_OK;
474
475
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
476
0
    return ERROR_INVALID_DATA;
477
478
0
  Stream_Read_UINT32(s, FramesPerPacket);
479
0
  Stream_Read_UINT32(s, initialFormat);
480
0
  WLog_Print(audin->log, WLOG_DEBUG, "FramesPerPacket=%" PRIu32 " initialFormat=%" PRIu32 "",
481
0
             FramesPerPacket, initialFormat);
482
0
  audin->FramesPerPacket = FramesPerPacket;
483
484
0
  if (initialFormat >= callback->formats_count)
485
0
  {
486
0
    WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
487
0
               initialFormat, callback->formats_count);
488
0
    return ERROR_INVALID_DATA;
489
0
  }
490
491
0
  audin->format = &callback->formats[initialFormat];
492
493
0
  if (!audin_open_device(audin, callback))
494
0
    return ERROR_INTERNAL_ERROR;
495
496
0
  if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
497
0
  {
498
0
    WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
499
0
    return error;
500
0
  }
501
502
0
  if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
503
0
    WLog_Print(audin->log, WLOG_ERROR, "audin_send_open_reply_pdu failed!");
504
505
0
  return error;
506
0
}
507
508
/**
509
 * Function description
510
 *
511
 * @return 0 on success, otherwise a Win32 error code
512
 */
513
static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
514
                                        wStream* s)
515
0
{
516
0
  UINT32 NewFormat = 0;
517
0
  UINT error = CHANNEL_RC_OK;
518
519
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
520
0
    return ERROR_INVALID_DATA;
521
522
0
  Stream_Read_UINT32(s, NewFormat);
523
0
  WLog_Print(audin->log, WLOG_DEBUG, "NewFormat=%" PRIu32 "", NewFormat);
524
525
0
  if (NewFormat >= callback->formats_count)
526
0
  {
527
0
    WLog_Print(audin->log, WLOG_ERROR, "invalid format index %" PRIu32 " (total %" PRIu32 ")",
528
0
               NewFormat, callback->formats_count);
529
0
    return ERROR_INVALID_DATA;
530
0
  }
531
532
0
  audin->format = &callback->formats[NewFormat];
533
534
0
  if (audin->device)
535
0
  {
536
0
    IFCALLRET(audin->device->Close, error, audin->device);
537
538
0
    if (error != CHANNEL_RC_OK)
539
0
    {
540
0
      WLog_ERR(TAG, "Close failed with errorcode %" PRIu32 "", error);
541
0
      return error;
542
0
    }
543
0
  }
544
545
0
  if (!audin_open_device(audin, callback))
546
0
    return ERROR_INTERNAL_ERROR;
547
548
0
  if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
549
0
    WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
550
551
0
  return error;
552
0
}
553
554
/**
555
 * Function description
556
 *
557
 * @return 0 on success, otherwise a Win32 error code
558
 */
559
static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
560
0
{
561
0
  UINT error = 0;
562
0
  BYTE MessageId = 0;
563
0
  AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
564
565
0
  if (!callback || !data)
566
0
    return ERROR_INVALID_PARAMETER;
567
568
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
569
570
0
  if (!audin)
571
0
    return ERROR_INTERNAL_ERROR;
572
573
0
  if (!Stream_CheckAndLogRequiredCapacity(TAG, data, 1))
574
0
    return ERROR_NO_DATA;
575
576
0
  Stream_Read_UINT8(data, MessageId);
577
0
  WLog_Print(audin->log, WLOG_DEBUG, "MessageId=0x%02" PRIx8 "", MessageId);
578
579
0
  switch (MessageId)
580
0
  {
581
0
    case MSG_SNDIN_VERSION:
582
0
      error = audin_process_version(audin, callback, data);
583
0
      break;
584
585
0
    case MSG_SNDIN_FORMATS:
586
0
      error = audin_process_formats(audin, callback, data);
587
0
      break;
588
589
0
    case MSG_SNDIN_OPEN:
590
0
      error = audin_process_open(audin, callback, data);
591
0
      break;
592
593
0
    case MSG_SNDIN_FORMATCHANGE:
594
0
      error = audin_process_format_change(audin, callback, data);
595
0
      break;
596
597
0
    default:
598
0
      WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02" PRIx8 "", MessageId);
599
0
      error = ERROR_INVALID_DATA;
600
0
      break;
601
0
  }
602
603
0
  return error;
604
0
}
605
606
/**
607
 * Function description
608
 *
609
 * @return 0 on success, otherwise a Win32 error code
610
 */
611
static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
612
0
{
613
0
  AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*)pChannelCallback;
614
0
  WINPR_ASSERT(callback);
615
616
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)callback->plugin;
617
0
  WINPR_ASSERT(audin);
618
619
0
  UINT error = CHANNEL_RC_OK;
620
0
  WLog_Print(audin->log, WLOG_TRACE, "...");
621
622
0
  if (audin->device)
623
0
  {
624
0
    IFCALLRET(audin->device->Close, error, audin->device);
625
626
0
    if (error != CHANNEL_RC_OK)
627
0
      WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %" PRIu32 "", error);
628
0
  }
629
630
0
  audin->format = NULL;
631
0
  audio_formats_free(callback->formats, callback->formats_count);
632
0
  free(callback);
633
0
  return error;
634
0
}
635
636
/**
637
 * Function description
638
 *
639
 * @return 0 on success, otherwise a Win32 error code
640
 */
641
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
642
                                            IWTSVirtualChannel* pChannel, BYTE* Data,
643
                                            BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
644
0
{
645
0
  GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
646
647
0
  if (!listener_callback || !listener_callback->plugin)
648
0
    return ERROR_INTERNAL_ERROR;
649
650
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)listener_callback->plugin;
651
0
  WLog_Print(audin->log, WLOG_TRACE, "...");
652
0
  AUDIN_CHANNEL_CALLBACK* callback =
653
0
      (AUDIN_CHANNEL_CALLBACK*)calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
654
655
0
  if (!callback)
656
0
  {
657
0
    WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
658
0
    return CHANNEL_RC_NO_MEMORY;
659
0
  }
660
661
0
  callback->iface.OnDataReceived = audin_on_data_received;
662
0
  callback->iface.OnClose = audin_on_close;
663
0
  callback->plugin = listener_callback->plugin;
664
0
  callback->channel_mgr = listener_callback->channel_mgr;
665
0
  callback->channel = pChannel;
666
0
  *ppCallback = (IWTSVirtualChannelCallback*)callback;
667
0
  return CHANNEL_RC_OK;
668
0
}
669
670
/**
671
 * Function description
672
 *
673
 * @return 0 on success, otherwise a Win32 error code
674
 */
675
static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
676
0
{
677
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
678
679
0
  if (!audin)
680
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
681
682
0
  if (!pChannelMgr)
683
0
    return ERROR_INVALID_PARAMETER;
684
685
0
  if (audin->initialized)
686
0
  {
687
0
    WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AUDIN_DVC_CHANNEL_NAME);
688
0
    return ERROR_INVALID_DATA;
689
0
  }
690
691
0
  WLog_Print(audin->log, WLOG_TRACE, "...");
692
0
  audin->listener_callback =
693
0
      (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
694
695
0
  if (!audin->listener_callback)
696
0
  {
697
0
    WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
698
0
    return CHANNEL_RC_NO_MEMORY;
699
0
  }
700
701
0
  audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
702
0
  audin->listener_callback->plugin = pPlugin;
703
0
  audin->listener_callback->channel_mgr = pChannelMgr;
704
0
  const UINT rc = pChannelMgr->CreateListener(pChannelMgr, AUDIN_DVC_CHANNEL_NAME, 0,
705
0
                                              &audin->listener_callback->iface, &audin->listener);
706
707
0
  audin->initialized = rc == CHANNEL_RC_OK;
708
0
  return rc;
709
0
}
710
711
/**
712
 * Function description
713
 *
714
 * @return 0 on success, otherwise a Win32 error code
715
 */
716
static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
717
0
{
718
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
719
0
  UINT error = CHANNEL_RC_OK;
720
721
0
  if (!audin)
722
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
723
724
0
  WLog_Print(audin->log, WLOG_TRACE, "...");
725
726
0
  if (audin->listener_callback)
727
0
  {
728
0
    IWTSVirtualChannelManager* mgr = audin->listener_callback->channel_mgr;
729
0
    if (mgr)
730
0
      IFCALL(mgr->DestroyListener, mgr, audin->listener);
731
0
  }
732
0
  audio_formats_free(audin->fixed_format, 1);
733
734
0
  if (audin->device)
735
0
  {
736
0
    IFCALLRET(audin->device->Free, error, audin->device);
737
738
0
    if (error != CHANNEL_RC_OK)
739
0
    {
740
0
      WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %" PRIu32 "", error);
741
      // dont stop on error
742
0
    }
743
744
0
    audin->device = NULL;
745
0
  }
746
747
0
  freerdp_dsp_context_free(audin->dsp_context);
748
0
  Stream_Free(audin->data, TRUE);
749
0
  free(audin->subsystem);
750
0
  free(audin->device_name);
751
0
  free(audin->listener_callback);
752
0
  free(audin);
753
0
  return CHANNEL_RC_OK;
754
0
}
755
756
static UINT audin_plugin_attached(IWTSPlugin* pPlugin)
757
0
{
758
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
759
0
  UINT error = CHANNEL_RC_OK;
760
761
0
  if (!audin)
762
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
763
764
0
  audin->attached = TRUE;
765
0
  return error;
766
0
}
767
768
static UINT audin_plugin_detached(IWTSPlugin* pPlugin)
769
0
{
770
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
771
0
  UINT error = CHANNEL_RC_OK;
772
773
0
  if (!audin)
774
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
775
776
0
  audin->attached = FALSE;
777
0
  return error;
778
0
}
779
780
/**
781
 * Function description
782
 *
783
 * @return 0 on success, otherwise a Win32 error code
784
 */
785
static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
786
0
{
787
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pPlugin;
788
789
0
  WINPR_ASSERT(audin);
790
791
0
  if (audin->device)
792
0
  {
793
0
    WLog_Print(audin->log, WLOG_ERROR, "existing device, abort.");
794
0
    return ERROR_ALREADY_EXISTS;
795
0
  }
796
797
0
  WLog_Print(audin->log, WLOG_DEBUG, "device registered.");
798
0
  audin->device = device;
799
0
  return CHANNEL_RC_OK;
800
0
}
801
802
/**
803
 * Function description
804
 *
805
 * @return 0 on success, otherwise a Win32 error code
806
 */
807
static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, const char* name, const ADDIN_ARGV* args)
808
0
{
809
0
  WINPR_ASSERT(audin);
810
811
0
  FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints = { 0 };
812
0
  UINT error = ERROR_INTERNAL_ERROR;
813
0
  const PFREERDP_AUDIN_DEVICE_ENTRY entry =
814
0
      (const PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_channel_addin_entry(AUDIN_CHANNEL_NAME,
815
0
                                                                          name, NULL, 0);
816
817
0
  if (entry == NULL)
818
0
  {
819
0
    WLog_Print(audin->log, WLOG_ERROR,
820
0
               "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
821
0
               name);
822
0
    return ERROR_INVALID_FUNCTION;
823
0
  }
824
825
0
  entryPoints.plugin = &audin->iface;
826
0
  entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
827
0
  entryPoints.args = args;
828
0
  entryPoints.rdpcontext = audin->rdpcontext;
829
830
0
  if ((error = entry(&entryPoints)))
831
0
  {
832
0
    WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %" PRIu32 ".", name, error);
833
0
    return error;
834
0
  }
835
836
0
  WLog_Print(audin->log, WLOG_INFO, "Loaded %s backend for audin", name);
837
0
  return CHANNEL_RC_OK;
838
0
}
839
840
/**
841
 * Function description
842
 *
843
 * @return 0 on success, otherwise a Win32 error code
844
 */
845
static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
846
0
{
847
0
  WINPR_ASSERT(audin);
848
849
0
  free(audin->subsystem);
850
0
  audin->subsystem = _strdup(subsystem);
851
852
0
  if (!audin->subsystem)
853
0
  {
854
0
    WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
855
0
    return ERROR_NOT_ENOUGH_MEMORY;
856
0
  }
857
858
0
  return CHANNEL_RC_OK;
859
0
}
860
861
/**
862
 * Function description
863
 *
864
 * @return 0 on success, otherwise a Win32 error code
865
 */
866
static UINT audin_set_device_name(AUDIN_PLUGIN* audin, const char* device_name)
867
0
{
868
0
  WINPR_ASSERT(audin);
869
870
0
  free(audin->device_name);
871
0
  audin->device_name = _strdup(device_name);
872
873
0
  if (!audin->device_name)
874
0
  {
875
0
    WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
876
0
    return ERROR_NOT_ENOUGH_MEMORY;
877
0
  }
878
879
0
  return CHANNEL_RC_OK;
880
0
}
881
882
BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, const ADDIN_ARGV* args)
883
0
{
884
0
  COMMAND_LINE_ARGUMENT_A audin_args[] = {
885
0
    { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
886
0
    { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
887
0
    { "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", NULL, NULL, -1, NULL, "format" },
888
0
    { "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", NULL, NULL, -1, NULL, "rate" },
889
0
    { "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", NULL, NULL, -1, NULL, "channel" },
890
0
    { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
891
0
  };
892
893
0
  if (!args || args->argc == 1)
894
0
    return TRUE;
895
896
0
  const DWORD flags =
897
0
      COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
898
0
  const int status =
899
0
      CommandLineParseArgumentsA(args->argc, args->argv, audin_args, flags, audin, NULL, NULL);
900
901
0
  if (status != 0)
902
0
    return FALSE;
903
904
0
  const COMMAND_LINE_ARGUMENT_A* arg = audin_args;
905
0
  errno = 0;
906
907
0
  do
908
0
  {
909
0
    if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
910
0
      continue;
911
912
0
    CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
913
0
    {
914
0
      const UINT error = audin_set_subsystem(audin, arg->Value);
915
0
      if (error != CHANNEL_RC_OK)
916
0
      {
917
0
        WLog_Print(audin->log, WLOG_ERROR,
918
0
                   "audin_set_subsystem failed with error %" PRIu32 "!", error);
919
0
        return FALSE;
920
0
      }
921
0
    }
922
0
    CommandLineSwitchCase(arg, "dev")
923
0
    {
924
0
      const UINT error = audin_set_device_name(audin, arg->Value);
925
0
      if (error != CHANNEL_RC_OK)
926
0
      {
927
0
        WLog_Print(audin->log, WLOG_ERROR,
928
0
                   "audin_set_device_name failed with error %" PRIu32 "!", error);
929
0
        return FALSE;
930
0
      }
931
0
    }
932
0
    CommandLineSwitchCase(arg, "format")
933
0
    {
934
0
      unsigned long val = strtoul(arg->Value, NULL, 0);
935
936
0
      if ((errno != 0) || (val > UINT16_MAX))
937
0
        return FALSE;
938
939
0
      audin->fixed_format->wFormatTag = (UINT16)val;
940
0
    }
941
0
    CommandLineSwitchCase(arg, "rate")
942
0
    {
943
0
      long val = strtol(arg->Value, NULL, 0);
944
945
0
      if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
946
0
        return FALSE;
947
948
0
      audin->fixed_format->nSamplesPerSec = val;
949
0
    }
950
0
    CommandLineSwitchCase(arg, "channel")
951
0
    {
952
0
      unsigned long val = strtoul(arg->Value, NULL, 0);
953
954
0
      if ((errno != 0) || (val <= UINT16_MAX))
955
0
        audin->fixed_format->nChannels = (UINT16)val;
956
0
    }
957
0
    CommandLineSwitchDefault(arg)
958
0
    {
959
0
    }
960
0
    CommandLineSwitchEnd(arg)
961
0
  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
962
963
0
  return TRUE;
964
0
}
965
966
/**
967
 * Function description
968
 *
969
 * @return 0 on success, otherwise a Win32 error code
970
 */
971
FREERDP_ENTRY_POINT(UINT audin_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
972
0
{
973
0
  struct SubsystemEntry
974
0
  {
975
0
    char* subsystem;
976
0
    char* device;
977
0
  };
978
0
  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
979
0
  struct SubsystemEntry entries[] =
980
0
  {
981
#if defined(WITH_PULSE)
982
    { "pulse", "" },
983
#endif
984
0
#if defined(WITH_OSS)
985
0
    { "oss", "default" },
986
0
#endif
987
#if defined(WITH_ALSA)
988
    { "alsa", "default" },
989
#endif
990
#if defined(WITH_OPENSLES)
991
    { "opensles", "default" },
992
#endif
993
#if defined(WITH_WINMM)
994
    { "winmm", "default" },
995
#endif
996
#if defined(WITH_MACAUDIO)
997
    { "mac", "default" },
998
#endif
999
#if defined(WITH_IOSAUDIO)
1000
    { "ios", "default" },
1001
#endif
1002
#if defined(WITH_SNDIO)
1003
    { "sndio", "default" },
1004
#endif
1005
0
    { NULL, NULL }
1006
0
  };
1007
0
  struct SubsystemEntry* entry = &entries[0];
1008
0
  WINPR_ASSERT(pEntryPoints);
1009
0
  WINPR_ASSERT(pEntryPoints->GetPlugin);
1010
0
  AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, AUDIN_CHANNEL_NAME);
1011
1012
0
  if (audin != NULL)
1013
0
    return CHANNEL_RC_ALREADY_INITIALIZED;
1014
1015
0
  audin = (AUDIN_PLUGIN*)calloc(1, sizeof(AUDIN_PLUGIN));
1016
1017
0
  if (!audin)
1018
0
  {
1019
0
    WLog_ERR(TAG, "calloc failed!");
1020
0
    return CHANNEL_RC_NO_MEMORY;
1021
0
  }
1022
1023
0
  audin->log = WLog_Get(TAG);
1024
0
  audin->data = Stream_New(NULL, 4096);
1025
0
  audin->fixed_format = audio_format_new();
1026
1027
0
  if (!audin->fixed_format)
1028
0
    goto out;
1029
1030
0
  if (!audin->data)
1031
0
    goto out;
1032
1033
0
  audin->dsp_context = freerdp_dsp_context_new(TRUE);
1034
1035
0
  if (!audin->dsp_context)
1036
0
    goto out;
1037
1038
0
  audin->attached = TRUE;
1039
0
  audin->iface.Initialize = audin_plugin_initialize;
1040
0
  audin->iface.Connected = NULL;
1041
0
  audin->iface.Disconnected = NULL;
1042
0
  audin->iface.Terminated = audin_plugin_terminated;
1043
0
  audin->iface.Attached = audin_plugin_attached;
1044
0
  audin->iface.Detached = audin_plugin_detached;
1045
1046
0
  const ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
1047
0
  audin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
1048
1049
0
  if (args)
1050
0
  {
1051
0
    if (!audin_process_addin_args(audin, args))
1052
0
      goto out;
1053
0
  }
1054
1055
0
  if (audin->subsystem)
1056
0
  {
1057
0
    if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1058
0
    {
1059
0
      WLog_Print(
1060
0
          audin->log, WLOG_ERROR,
1061
0
          "Unable to load microphone redirection subsystem %s because of error %" PRIu32 "",
1062
0
          audin->subsystem, error);
1063
0
      goto out;
1064
0
    }
1065
0
  }
1066
0
  else
1067
0
  {
1068
0
    while (entry && entry->subsystem && !audin->device)
1069
0
    {
1070
0
      if ((error = audin_set_subsystem(audin, entry->subsystem)))
1071
0
      {
1072
0
        WLog_Print(audin->log, WLOG_ERROR,
1073
0
                   "audin_set_subsystem for %s failed with error %" PRIu32 "!",
1074
0
                   entry->subsystem, error);
1075
0
      }
1076
0
      else if ((error = audin_set_device_name(audin, entry->device)))
1077
0
      {
1078
0
        WLog_Print(audin->log, WLOG_ERROR,
1079
0
                   "audin_set_device_name for %s failed with error %" PRIu32 "!",
1080
0
                   entry->subsystem, error);
1081
0
      }
1082
0
      else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
1083
0
      {
1084
0
        WLog_Print(audin->log, WLOG_ERROR,
1085
0
                   "audin_load_device_plugin %s failed with error %" PRIu32 "!",
1086
0
                   entry->subsystem, error);
1087
0
      }
1088
1089
0
      entry++;
1090
0
    }
1091
0
  }
1092
1093
0
  if (audin->device == NULL)
1094
0
  {
1095
    /* If we have no audin device do not register plugin but still return OK or the client will
1096
     * just disconnect due to a missing microphone. */
1097
0
    WLog_Print(audin->log, WLOG_ERROR, "No microphone device could be found.");
1098
0
    error = CHANNEL_RC_OK;
1099
0
    goto out;
1100
0
  }
1101
1102
0
  error = pEntryPoints->RegisterPlugin(pEntryPoints, AUDIN_CHANNEL_NAME, &audin->iface);
1103
0
  if (error == CHANNEL_RC_OK)
1104
0
    return error;
1105
1106
0
out:
1107
0
  audin_plugin_terminated(&audin->iface);
1108
0
  return error;
1109
0
}