Coverage Report

Created: 2026-07-16 07:09

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