FreeRDP
Loading...
Searching...
No Matches
client/rdpsnd_main.c
1
25#include <freerdp/config.h>
26
27#ifndef _WIN32
28#include <sys/time.h>
29#include <signal.h>
30#endif
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36
37#include <winpr/crt.h>
38#include <winpr/assert.h>
39#include <winpr/wlog.h>
40#include <winpr/stream.h>
41#include <winpr/cmdline.h>
42#include <winpr/sysinfo.h>
43#include <winpr/collections.h>
44
45#include <freerdp/types.h>
46#include <freerdp/addin.h>
47#include <freerdp/freerdp.h>
48#include <freerdp/codec/dsp.h>
49#include <freerdp/client/channels.h>
50
51#include "rdpsnd_common.h"
52#include "rdpsnd_main.h"
53
54struct rdpsnd_plugin
55{
56 IWTSPlugin iface;
57 IWTSListener* listener;
58 GENERIC_LISTENER_CALLBACK* listener_callback;
59
60 CHANNEL_DEF channelDef;
61 CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
62
63 wStreamPool* pool;
64 wStream* data_in;
65
66 void* InitHandle;
67 DWORD OpenHandle;
68
69 wLog* log;
70
71 BYTE cBlockNo;
72 UINT16 wQualityMode;
73 UINT16 wCurrentFormatNo;
74
75 AUDIO_FORMAT* ServerFormats;
76 UINT16 NumberOfServerFormats;
77
78 AUDIO_FORMAT* ClientFormats;
79 UINT16 NumberOfClientFormats;
80
81 BOOL attached;
82 BOOL dynamic;
83
84 BOOL expectingWave;
85 BYTE waveData[4];
86 UINT16 waveDataSize;
87 UINT16 wTimeStamp;
88 UINT64 wArrivalTime;
89
90 UINT32 latency;
91 BOOL isOpen;
92 AUDIO_FORMAT* fixed_format;
93
94 UINT32 startPlayTime;
95 size_t totalPlaySize;
96
97 char* subsystem;
98 char* device_name;
99
100 /* Device plugin */
101 rdpsndDevicePlugin* device;
102 rdpContext* rdpcontext;
103
104 FREERDP_DSP_CONTEXT* dsp_context;
105
106 HANDLE thread;
107 wMessageQueue* queue;
108 CRITICAL_SECTION asyncLock;
109 BOOL initialized;
110
111 UINT16 wVersion;
112 UINT32 volume;
113 BOOL applyVolume;
114
115 size_t references;
116 BOOL OnOpenCalled;
117 BOOL async;
118 BOOL firstFlagReceived;
119 UINT32 totalLength;
120 BOOL connected;
121 UINT64 badWaves; /* waves dropped because they did not decode */
122};
123
124WINPR_ATTR_NODISCARD
125static DWORD WINAPI play_thread(LPVOID arg);
126
127WINPR_ATTR_NODISCARD
128static const char* rdpsnd_is_dyn_str(BOOL dynamic)
129{
130 if (dynamic)
131 return "[dynamic]";
132 return "[static]";
133}
134
135static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd);
136
142WINPR_ATTR_NODISCARD
143static UINT rdpsnd_virtual_channel_write(rdpsndPlugin* rdpsnd, wStream* s);
144
150WINPR_ATTR_NODISCARD
151static UINT rdpsnd_send_quality_mode_pdu(rdpsndPlugin* rdpsnd)
152{
153 wStream* pdu = nullptr;
154 WINPR_ASSERT(rdpsnd);
155 pdu = Stream_New(nullptr, 8);
156
157 if (!pdu)
158 {
159 WLog_ERR(TAG, "%s Stream_New failed!", rdpsnd_is_dyn_str(rdpsnd->dynamic));
160 return CHANNEL_RC_NO_MEMORY;
161 }
162
163 Stream_Write_UINT8(pdu, SNDC_QUALITYMODE); /* msgType */
164 Stream_Write_UINT8(pdu, 0); /* bPad */
165 Stream_Write_UINT16(pdu, 4); /* BodySize */
166 Stream_Write_UINT16(pdu, rdpsnd->wQualityMode); /* wQualityMode */
167 Stream_Write_UINT16(pdu, 0); /* Reserved */
168 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s QualityMode: %" PRIu16 "",
169 rdpsnd_is_dyn_str(rdpsnd->dynamic), rdpsnd->wQualityMode);
170 return rdpsnd_virtual_channel_write(rdpsnd, pdu);
171}
172
173static void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd)
174{
175 WINPR_ASSERT(rdpsnd);
176 audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
177 rdpsnd->NumberOfClientFormats = 0;
178 rdpsnd->ClientFormats = nullptr;
179
180 if (!rdpsnd->NumberOfServerFormats)
181 return;
182
183 rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats);
184
185 if (!rdpsnd->ClientFormats || !rdpsnd->device)
186 return;
187
188 size_t compatible = 0;
189 for (UINT16 index = 0; index < rdpsnd->NumberOfServerFormats; index++)
190 {
191 const AUDIO_FORMAT* serverFormat = &rdpsnd->ServerFormats[index];
192
193 if (!audio_format_compatible(rdpsnd->fixed_format, serverFormat))
194 continue;
195
196 compatible++;
197 WINPR_ASSERT(rdpsnd->device->FormatSupported);
198 if (freerdp_dsp_supports_format(serverFormat, FALSE) ||
199 rdpsnd->device->FormatSupported(rdpsnd->device, serverFormat))
200 {
201 AUDIO_FORMAT* clientFormat = &rdpsnd->ClientFormats[rdpsnd->NumberOfClientFormats++];
202 audio_format_copy(serverFormat, clientFormat);
203 }
204 }
205
206 if (compatible == 0)
207 {
208 WLog_Print(rdpsnd->log, WLOG_WARN,
209 "%s server offers no format matching /sound format:%s (%" PRIu16
210 "),rate:%" PRIu32 ",channel:%" PRIu16 ", audio output disabled",
211 rdpsnd_is_dyn_str(rdpsnd->dynamic),
212 audio_format_get_tag_string(rdpsnd->fixed_format->wFormatTag),
213 rdpsnd->fixed_format->wFormatTag, rdpsnd->fixed_format->nSamplesPerSec,
214 rdpsnd->fixed_format->nChannels);
215 }
216}
217
223WINPR_ATTR_NODISCARD
224static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd)
225{
226 wStream* pdu = nullptr;
227 size_t length = 0;
228 UINT32 dwVolume = 0;
229 UINT16 wNumberOfFormats = 0;
230 WINPR_ASSERT(rdpsnd);
231
232 if (!rdpsnd->device || (!rdpsnd->dynamic && (rdpsnd->OpenHandle == 0)))
233 return CHANNEL_RC_INITIALIZATION_ERROR;
234
235 dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device);
236 wNumberOfFormats = rdpsnd->NumberOfClientFormats;
237 length = 4 + 20;
238
239 for (UINT16 index = 0; index < wNumberOfFormats; index++)
240 length += (18 + rdpsnd->ClientFormats[index].cbSize);
241
242 /* BodySize is a UINT16 field, so the whole PDU must fit in UINT16_MAX. A server can inflate
243 * the client format list through large per-format cbSize values; without this bound the
244 * accumulator wrapped and Stream_New() below was undersized. Mirrors the server side check in
245 * rdpsnd_server_send_formats(). */
246 if (length > UINT16_MAX)
247 return ERROR_INVALID_DATA;
248
249 pdu = Stream_New(nullptr, length);
250
251 if (!pdu)
252 {
253 WLog_ERR(TAG, "%s Stream_New failed!", rdpsnd_is_dyn_str(rdpsnd->dynamic));
254 return CHANNEL_RC_NO_MEMORY;
255 }
256
257 Stream_Write_UINT8(pdu, SNDC_FORMATS); /* msgType */
258 Stream_Write_UINT8(pdu, 0); /* bPad */
259 Stream_Write_UINT16(pdu, (UINT16)(length - 4)); /* BodySize */
260 Stream_Write_UINT32(pdu, TSSNDCAPS_ALIVE | TSSNDCAPS_VOLUME); /* dwFlags */
261 Stream_Write_UINT32(pdu, dwVolume); /* dwVolume */
262 Stream_Write_UINT32(pdu, 0); /* dwPitch */
263 Stream_Write_UINT16(pdu, 0); /* wDGramPort */
264 Stream_Write_UINT16(pdu, wNumberOfFormats); /* wNumberOfFormats */
265 Stream_Write_UINT8(pdu, 0); /* cLastBlockConfirmed */
266 Stream_Write_UINT16(pdu, CHANNEL_VERSION_WIN_MAX); /* wVersion */
267 Stream_Write_UINT8(pdu, 0); /* bPad */
268
269 for (UINT16 index = 0; index < wNumberOfFormats; index++)
270 {
271 const AUDIO_FORMAT* clientFormat = &rdpsnd->ClientFormats[index];
272
273 if (!audio_format_write(pdu, clientFormat))
274 {
275 Stream_Free(pdu, TRUE);
276 return ERROR_INTERNAL_ERROR;
277 }
278 }
279
280 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Client Audio Formats",
281 rdpsnd_is_dyn_str(rdpsnd->dynamic));
282 return rdpsnd_virtual_channel_write(rdpsnd, pdu);
283}
284
290WINPR_ATTR_NODISCARD
291static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s)
292{
293 UINT16 wNumberOfFormats = 0;
294 UINT ret = ERROR_BAD_LENGTH;
295
296 WINPR_ASSERT(rdpsnd);
297 audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
298 rdpsnd->NumberOfServerFormats = 0;
299 rdpsnd->ServerFormats = nullptr;
300
301 if (!Stream_CheckAndLogRequiredLength(TAG, s, 30))
302 return ERROR_BAD_LENGTH;
303
304 /* http://msdn.microsoft.com/en-us/library/cc240956.aspx */
305 Stream_Seek_UINT32(s); /* dwFlags */
306 Stream_Seek_UINT32(s); /* dwVolume */
307 Stream_Seek_UINT32(s); /* dwPitch */
308 Stream_Seek_UINT16(s); /* wDGramPort */
309 Stream_Read_UINT16(s, wNumberOfFormats);
310 Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */
311 Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */
312 Stream_Seek_UINT8(s); /* bPad */
313
314 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, wNumberOfFormats, 14ull))
315 return ERROR_BAD_LENGTH;
316
317 if (wNumberOfFormats > 0)
318 {
319 rdpsnd->ServerFormats = audio_formats_new(wNumberOfFormats);
320
321 if (!rdpsnd->ServerFormats)
322 return CHANNEL_RC_NO_MEMORY;
323
324 rdpsnd->NumberOfServerFormats = wNumberOfFormats;
325
326 for (UINT16 index = 0; index < wNumberOfFormats; index++)
327 {
328 AUDIO_FORMAT* format = &rdpsnd->ServerFormats[index];
329
330 if (!audio_format_read(s, format))
331 goto out_fail;
332 }
333 }
334
335 WINPR_ASSERT(rdpsnd->device);
336 ret = IFCALLRESULT(CHANNEL_RC_OK, rdpsnd->device->ServerFormatAnnounce, rdpsnd->device,
337 rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
338 if (ret != CHANNEL_RC_OK)
339 goto out_fail;
340
341 rdpsnd_select_supported_audio_formats(rdpsnd);
342 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Server Audio Formats",
343 rdpsnd_is_dyn_str(rdpsnd->dynamic));
344 ret = rdpsnd_send_client_audio_formats(rdpsnd);
345
346 if (ret == CHANNEL_RC_OK)
347 {
348 if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7)
349 ret = rdpsnd_send_quality_mode_pdu(rdpsnd);
350 }
351
352 return ret;
353out_fail:
354 audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
355 rdpsnd->ServerFormats = nullptr;
356 rdpsnd->NumberOfServerFormats = 0;
357 return ret;
358}
359
365WINPR_ATTR_NODISCARD
366static UINT rdpsnd_send_training_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp,
367 UINT16 wPackSize)
368{
369 wStream* pdu = nullptr;
370 WINPR_ASSERT(rdpsnd);
371 pdu = Stream_New(nullptr, 8);
372
373 if (!pdu)
374 {
375 WLog_ERR(TAG, "%s Stream_New failed!", rdpsnd_is_dyn_str(rdpsnd->dynamic));
376 return CHANNEL_RC_NO_MEMORY;
377 }
378
379 Stream_Write_UINT8(pdu, SNDC_TRAINING); /* msgType */
380 Stream_Write_UINT8(pdu, 0); /* bPad */
381 Stream_Write_UINT16(pdu, 4); /* BodySize */
382 Stream_Write_UINT16(pdu, wTimeStamp);
383 Stream_Write_UINT16(pdu, wPackSize);
384 WLog_Print(rdpsnd->log, WLOG_DEBUG,
385 "%s Training Response: wTimeStamp: %" PRIu16 " wPackSize: %" PRIu16 "",
386 rdpsnd_is_dyn_str(rdpsnd->dynamic), wTimeStamp, wPackSize);
387 return rdpsnd_virtual_channel_write(rdpsnd, pdu);
388}
389
395WINPR_ATTR_NODISCARD
396static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s)
397{
398 UINT16 wTimeStamp = 0;
399 UINT16 wPackSize = 0;
400 WINPR_ASSERT(rdpsnd);
401 WINPR_ASSERT(s);
402
403 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
404 return ERROR_BAD_LENGTH;
405
406 Stream_Read_UINT16(s, wTimeStamp);
407 Stream_Read_UINT16(s, wPackSize);
408 WLog_Print(rdpsnd->log, WLOG_DEBUG,
409 "%s Training Request: wTimeStamp: %" PRIu16 " wPackSize: %" PRIu16 "",
410 rdpsnd_is_dyn_str(rdpsnd->dynamic), wTimeStamp, wPackSize);
411 return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize);
412}
413
414WINPR_ATTR_NODISCARD
415static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd)
416{
417 WINPR_ASSERT(rdpsnd);
418
419 if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device)
420 {
421 BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume);
422 if (!rc)
423 return FALSE;
424 rdpsnd->applyVolume = FALSE;
425 }
426 return TRUE;
427}
428
429WINPR_ATTR_NODISCARD
430static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT16 wFormatNo,
431 const AUDIO_FORMAT* format)
432{
433 if (!rdpsnd)
434 return FALSE;
435 WINPR_ASSERT(format);
436
437 if (!rdpsnd->isOpen || (wFormatNo != rdpsnd->wCurrentFormatNo))
438 {
439 BOOL rc = 0;
440 BOOL supported = 0;
441 AUDIO_FORMAT deviceFormat = *format;
442
443 IFCALL(rdpsnd->device->Close, rdpsnd->device);
444 supported = IFCALLRESULT(FALSE, rdpsnd->device->FormatSupported, rdpsnd->device, format);
445
446 if (!supported)
447 {
448 if (!IFCALLRESULT(FALSE, rdpsnd->device->DefaultFormat, rdpsnd->device, format,
449 &deviceFormat))
450 {
451 deviceFormat.wFormatTag = WAVE_FORMAT_PCM;
452 deviceFormat.wBitsPerSample = 16;
453 deviceFormat.cbSize = 0;
454 }
455 }
456
457 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Opening device with format %s [backend %s]",
458 rdpsnd_is_dyn_str(rdpsnd->dynamic),
459 audio_format_get_tag_string(format->wFormatTag),
460 audio_format_get_tag_string(deviceFormat.wFormatTag));
461 rc = IFCALLRESULT(FALSE, rdpsnd->device->Open, rdpsnd->device, &deviceFormat,
462 rdpsnd->latency);
463
464 if (!rc)
465 return FALSE;
466
467 if (!supported)
468 {
469 if (!freerdp_dsp_context_reset(rdpsnd->dsp_context, format, 0u))
470 return FALSE;
471 }
472
473 rdpsnd->isOpen = TRUE;
474 rdpsnd->wCurrentFormatNo = wFormatNo;
475 rdpsnd->startPlayTime = 0;
476 rdpsnd->totalPlaySize = 0;
477 }
478
479 return rdpsnd_apply_volume(rdpsnd);
480}
481
487WINPR_ATTR_NODISCARD
488static UINT rdpsnd_recv_wave_info_pdu(rdpsndPlugin* rdpsnd, wStream* s, UINT16 BodySize)
489{
490 UINT16 wFormatNo = 0;
491 const AUDIO_FORMAT* format = nullptr;
492 WINPR_ASSERT(rdpsnd);
493 WINPR_ASSERT(s);
494
495 if (!Stream_CheckAndLogRequiredLength(TAG, s, 12) || (BodySize < 8))
496 return ERROR_BAD_LENGTH;
497
498 rdpsnd->wArrivalTime = GetTickCount64();
499 Stream_Read_UINT16(s, rdpsnd->wTimeStamp);
500 Stream_Read_UINT16(s, wFormatNo);
501
502 if (wFormatNo >= rdpsnd->NumberOfClientFormats)
503 return ERROR_INVALID_DATA;
504
505 Stream_Read_UINT8(s, rdpsnd->cBlockNo);
506 Stream_Seek(s, 3); /* bPad */
507 Stream_Read(s, rdpsnd->waveData, 4);
508 rdpsnd->waveDataSize = BodySize - 8;
509 format = &rdpsnd->ClientFormats[wFormatNo];
510 WLog_Print(rdpsnd->log, WLOG_DEBUG,
511 "%s WaveInfo: cBlockNo: %" PRIu8 " wFormatNo: %" PRIu16 " [%s]",
512 rdpsnd_is_dyn_str(rdpsnd->dynamic), rdpsnd->cBlockNo, wFormatNo,
513 audio_format_get_tag_string(format->wFormatTag));
514
515 if (!rdpsnd_ensure_device_is_open(rdpsnd, wFormatNo, format))
516 return ERROR_INTERNAL_ERROR;
517
518 rdpsnd->expectingWave = TRUE;
519 return CHANNEL_RC_OK;
520}
521
527WINPR_ATTR_NODISCARD
528static UINT rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp,
529 BYTE cConfirmedBlockNo)
530{
531 wStream* pdu = nullptr;
532 WINPR_ASSERT(rdpsnd);
533 pdu = Stream_New(nullptr, 8);
534
535 if (!pdu)
536 {
537 WLog_ERR(TAG, "%s Stream_New failed!", rdpsnd_is_dyn_str(rdpsnd->dynamic));
538 return CHANNEL_RC_NO_MEMORY;
539 }
540
541 Stream_Write_UINT8(pdu, SNDC_WAVECONFIRM);
542 Stream_Write_UINT8(pdu, 0);
543 Stream_Write_UINT16(pdu, 4);
544 Stream_Write_UINT16(pdu, wTimeStamp);
545 Stream_Write_UINT8(pdu, cConfirmedBlockNo); /* cConfirmedBlockNo */
546 Stream_Write_UINT8(pdu, 0); /* bPad */
547 return rdpsnd_virtual_channel_write(rdpsnd, pdu);
548}
549
550/* Audio the client may queue beyond the current sample before it drops
551 * samples as an overrun. The check is meant to stop old servers from
552 * queueing seconds of audio; jitter and bursts are tens of milliseconds. */
553#define RDPSND_OVERRUN_SLACK_MS 200
554
555WINPR_ATTR_NODISCARD
556static BOOL rdpsnd_detect_overrun(rdpsndPlugin* rdpsnd, const AUDIO_FORMAT* format, size_t size)
557{
558 UINT32 bpf = 0;
559 UINT32 now = 0;
560 UINT32 duration = 0;
561 UINT32 totalDuration = 0;
562 UINT32 remainingDuration = 0;
563 UINT32 maxDuration = 0;
564
565 if (!rdpsnd || !format)
566 return FALSE;
567
568 /* Older windows RDP servers do not limit the send buffer, which can
569 * cause quite a large amount of sound data buffered client side.
570 * If e.g. sound is paused server side the client will keep playing
571 * for a long time instead of pausing playback.
572 *
573 * To avoid this we check:
574 *
575 * 1. Is the sound sample received from a known format these servers
576 * support
577 * 2. If it is calculate the size of the client side sound buffer
578 * 3. If the buffer is too large silently drop the sample which will
579 * trigger a retransmit later on.
580 *
581 * This check must only be applied to these known formats, because
582 * with newer and other formats the sample size can not be calculated
583 * without decompressing the sample first.
584 */
585 switch (format->wFormatTag)
586 {
587 case WAVE_FORMAT_PCM:
588 case WAVE_FORMAT_DVI_ADPCM:
589 case WAVE_FORMAT_ADPCM:
590 case WAVE_FORMAT_ALAW:
591 case WAVE_FORMAT_MULAW:
592 break;
593 case WAVE_FORMAT_MSG723:
594 case WAVE_FORMAT_GSM610:
595 case WAVE_FORMAT_AAC_MS:
596 default:
597 return FALSE;
598 }
599
600 audio_format_print(WLog_Get(TAG), WLOG_DEBUG, format);
601 bpf = format->nChannels * format->wBitsPerSample * format->nSamplesPerSec / 8;
602 if (bpf == 0)
603 return FALSE;
604
605 duration = (UINT32)(1000 * size / bpf);
606 totalDuration = (UINT32)(1000 * rdpsnd->totalPlaySize / bpf);
607 now = GetTickCountPrecise();
608 if (rdpsnd->startPlayTime == 0)
609 {
610 rdpsnd->startPlayTime = now;
611 rdpsnd->totalPlaySize = size;
612 return FALSE;
613 }
614 else if (now - rdpsnd->startPlayTime > totalDuration + 10)
615 {
616 /* Buffer underrun */
617 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Buffer underrun by %u ms",
618 rdpsnd_is_dyn_str(rdpsnd->dynamic),
619 (UINT)(now - rdpsnd->startPlayTime - totalDuration));
620 rdpsnd->startPlayTime = now;
621 rdpsnd->totalPlaySize = size;
622 return FALSE;
623 }
624 else
625 {
626 /* Calculate remaining duration to be played */
627 remainingDuration = totalDuration - (now - rdpsnd->startPlayTime);
628
629 /* Maximum allow duration calculation: at least RDPSND_OVERRUN_SLACK_MS
630 * on top, so samples that arrive in a burst (a late one followed by
631 * an on-time one, or a server sending several at once) are kept */
632 maxDuration = duration * 2 + MAX(rdpsnd->latency, RDPSND_OVERRUN_SLACK_MS);
633
634 if (remainingDuration + duration > maxDuration)
635 {
636 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Buffer overrun pending %u ms dropping %u ms",
637 rdpsnd_is_dyn_str(rdpsnd->dynamic), remainingDuration, duration);
638 return TRUE;
639 }
640
641 rdpsnd->totalPlaySize += size;
642 return FALSE;
643 }
644}
645
646WINPR_ATTR_NODISCARD
647static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size)
648{
649 AUDIO_FORMAT* format = nullptr;
650 UINT64 end = 0;
651 UINT64 diffMS = 0;
652 UINT64 ts = 0;
653 UINT latency = 0;
654 UINT error = 0;
655
656 if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
657 return ERROR_BAD_LENGTH;
658
659 if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats)
660 return ERROR_INTERNAL_ERROR;
661
662 /*
663 * Send the first WaveConfirm PDU. The server side uses this to determine the
664 * network latency.
665 * See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU
666 */
667 error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo);
668 if (error)
669 return error;
670
671 const BYTE* data = Stream_ConstPointer(s);
672 format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo];
673 WLog_Print(rdpsnd->log, WLOG_DEBUG,
674 "%s Wave: cBlockNo: %" PRIu8 " wTimeStamp: %" PRIu16 ", size: %" PRIuz,
675 rdpsnd_is_dyn_str(rdpsnd->dynamic), rdpsnd->cBlockNo, rdpsnd->wTimeStamp, size);
676
677 if (rdpsnd->device && rdpsnd->attached && !rdpsnd_detect_overrun(rdpsnd, format, size))
678 {
679 wStream* pcmData = StreamPool_Take(rdpsnd->pool, 4096);
680
681 if (rdpsnd->device->FormatSupported(rdpsnd->device, format))
682 {
683 if (rdpsnd->device->PlayEx)
684 latency = rdpsnd->device->PlayEx(rdpsnd->device, format, data, size);
685 else
686 latency = IFCALLRESULT(0, rdpsnd->device->Play, rdpsnd->device, data, size);
687 }
688 else if (freerdp_dsp_decode(rdpsnd->dsp_context, format, data, size, pcmData))
689 {
690 Stream_SealLength(pcmData);
691
692 /* Decoders with priming delay (e.g. AAC) may legitimately produce no PCM yet */
693 if (Stream_Length(pcmData) == 0)
694 latency = 0;
695 else if (rdpsnd->device->PlayEx)
696 latency = rdpsnd->device->PlayEx(rdpsnd->device, format, Stream_Buffer(pcmData),
697 Stream_Length(pcmData));
698 else
699 latency = IFCALLRESULT(0, rdpsnd->device->Play, rdpsnd->device,
700 Stream_Buffer(pcmData), Stream_Length(pcmData));
701 }
702 else
703 {
704 /* Dropped, and confirmed like the others: an error here would end the
705 * thread that plays the sound, and with it the sound of the session */
706 rdpsnd->badWaves++;
707 WLog_Print(rdpsnd->log, (rdpsnd->badWaves == 1) ? WLOG_WARN : WLOG_DEBUG,
708 "%s dropped a %s wave of %" PRIuz " bytes that does not decode (%" PRIu64
709 " so far)",
710 rdpsnd_is_dyn_str(rdpsnd->dynamic),
711 audio_format_get_tag_string(format->wFormatTag), size, rdpsnd->badWaves);
712 }
713
714 Stream_Release(pcmData);
715 }
716
717 end = GetTickCount64();
718 diffMS = end - rdpsnd->wArrivalTime + latency;
719 ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX;
720
721 /*
722 * Send the second WaveConfirm PDU. With the first WaveConfirm PDU,
723 * the server side uses this second WaveConfirm PDU to determine the actual
724 * render latency.
725 */
726 return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo);
727}
728
734WINPR_ATTR_NODISCARD
735static UINT rdpsnd_recv_wave_pdu(rdpsndPlugin* rdpsnd, wStream* s)
736{
737 rdpsnd->expectingWave = FALSE;
738
745 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
746 return ERROR_INVALID_DATA;
747
748 CopyMemory(Stream_Buffer(s), rdpsnd->waveData, 4);
749 return rdpsnd_treat_wave(rdpsnd, s, rdpsnd->waveDataSize);
750}
751
752WINPR_ATTR_NODISCARD
753static UINT rdpsnd_recv_wave2_pdu(rdpsndPlugin* rdpsnd, wStream* s, UINT16 BodySize)
754{
755 UINT16 wFormatNo = 0;
756 AUDIO_FORMAT* format = nullptr;
757 UINT32 dwAudioTimeStamp = 0;
758
759 if (!Stream_CheckAndLogRequiredLength(TAG, s, 12) || (BodySize < 12))
760 return ERROR_BAD_LENGTH;
761
762 Stream_Read_UINT16(s, rdpsnd->wTimeStamp);
763 Stream_Read_UINT16(s, wFormatNo);
764 Stream_Read_UINT8(s, rdpsnd->cBlockNo);
765 Stream_Seek(s, 3); /* bPad */
766 Stream_Read_UINT32(s, dwAudioTimeStamp);
767 if (wFormatNo >= rdpsnd->NumberOfClientFormats)
768 return ERROR_INVALID_DATA;
769 format = &rdpsnd->ClientFormats[wFormatNo];
770 rdpsnd->waveDataSize = BodySize - 12;
771 rdpsnd->wArrivalTime = GetTickCount64();
772 WLog_Print(rdpsnd->log, WLOG_DEBUG,
773 "%s Wave2PDU: cBlockNo: %" PRIu8 " wFormatNo: %" PRIu16
774 " [%s] , align=%hu wTimeStamp=0x%04" PRIx16 ", dwAudioTimeStamp=0x%08" PRIx32,
775 rdpsnd_is_dyn_str(rdpsnd->dynamic), rdpsnd->cBlockNo, wFormatNo,
776 audio_format_get_tag_string(format->wFormatTag), format->nBlockAlign,
777 rdpsnd->wTimeStamp, dwAudioTimeStamp);
778
779 if (!rdpsnd_ensure_device_is_open(rdpsnd, wFormatNo, format))
780 return ERROR_INTERNAL_ERROR;
781
782 return rdpsnd_treat_wave(rdpsnd, s, rdpsnd->waveDataSize);
783}
784
785static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd)
786{
787 if (rdpsnd->isOpen)
788 {
789 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Closing device",
790 rdpsnd_is_dyn_str(rdpsnd->dynamic));
791 }
792 else
793 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Device already closed",
794 rdpsnd_is_dyn_str(rdpsnd->dynamic));
795}
796
802WINPR_ATTR_NODISCARD
803static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s)
804{
805 BOOL rc = TRUE;
806 UINT32 dwVolume = 0;
807
808 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
809 return ERROR_BAD_LENGTH;
810
811 Stream_Read_UINT32(s, dwVolume);
812 WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "",
813 rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume);
814
815 rdpsnd->volume = dwVolume;
816 rdpsnd->applyVolume = TRUE;
817 rc = rdpsnd_apply_volume(rdpsnd);
818
819 if (!rc)
820 {
821 WLog_ERR(TAG, "%s error setting volume", rdpsnd_is_dyn_str(rdpsnd->dynamic));
822 return CHANNEL_RC_INITIALIZATION_ERROR;
823 }
824
825 return CHANNEL_RC_OK;
826}
827
833WINPR_ATTR_NODISCARD
834static UINT rdpsnd_recv_pdu(rdpsndPlugin* rdpsnd, wStream* s)
835{
836 BYTE msgType = 0;
837 UINT16 BodySize = 0;
838 UINT status = CHANNEL_RC_OK;
839
840 if (rdpsnd->expectingWave)
841 {
842 status = rdpsnd_recv_wave_pdu(rdpsnd, s);
843 goto out;
844 }
845
846 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
847 {
848 status = ERROR_BAD_LENGTH;
849 goto out;
850 }
851
852 Stream_Read_UINT8(s, msgType); /* msgType */
853 Stream_Seek_UINT8(s); /* bPad */
854 Stream_Read_UINT16(s, BodySize);
855
856 switch (msgType)
857 {
858 case SNDC_FORMATS:
859 status = rdpsnd_recv_server_audio_formats_pdu(rdpsnd, s);
860 break;
861
862 case SNDC_TRAINING:
863 status = rdpsnd_recv_training_pdu(rdpsnd, s);
864 break;
865
866 case SNDC_WAVE:
867 status = rdpsnd_recv_wave_info_pdu(rdpsnd, s, BodySize);
868 break;
869
870 case SNDC_CLOSE:
871 rdpsnd_recv_close_pdu(rdpsnd);
872 break;
873
874 case SNDC_SETVOLUME:
875 status = rdpsnd_recv_volume_pdu(rdpsnd, s);
876 break;
877
878 case SNDC_WAVE2:
879 status = rdpsnd_recv_wave2_pdu(rdpsnd, s, BodySize);
880 break;
881
882 default:
883 WLog_ERR(TAG, "%s unknown msgType %" PRIu8 "", rdpsnd_is_dyn_str(rdpsnd->dynamic),
884 msgType);
885 break;
886 }
887
888out:
889 Stream_Release(s);
890 return status;
891}
892
893static void rdpsnd_register_device_plugin(rdpsndPlugin* rdpsnd, rdpsndDevicePlugin* device)
894{
895 if (rdpsnd->device)
896 {
897 WLog_ERR(TAG, "%s existing device, abort.", rdpsnd_is_dyn_str(FALSE));
898 return;
899 }
900
901 rdpsnd->device = device;
902 device->rdpsnd = rdpsnd;
903}
904
910WINPR_ATTR_NODISCARD
911static UINT rdpsnd_load_device_plugin(rdpsndPlugin* rdpsnd, const char* name,
912 const ADDIN_ARGV* args)
913{
914 FREERDP_RDPSND_DEVICE_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
915 UINT error = 0;
916 DWORD flags = FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX;
917 if (rdpsnd->dynamic)
918 flags = FREERDP_ADDIN_CHANNEL_DYNAMIC;
919 PVIRTUALCHANNELENTRY pvce =
920 freerdp_load_channel_addin_entry(RDPSND_CHANNEL_NAME, name, nullptr, flags);
921 PFREERDP_RDPSND_DEVICE_ENTRY entry = WINPR_FUNC_PTR_CAST(pvce, PFREERDP_RDPSND_DEVICE_ENTRY);
922
923 if (!entry)
924 return ERROR_INTERNAL_ERROR;
925
926 entryPoints.rdpsnd = rdpsnd;
927 entryPoints.pRegisterRdpsndDevice = rdpsnd_register_device_plugin;
928 entryPoints.args = args;
929
930 error = entry(&entryPoints);
931 if (error)
932 WLog_WARN(TAG, "%s %s entry returns error %" PRIu32 "", rdpsnd_is_dyn_str(rdpsnd->dynamic),
933 name, error);
934 else
935 WLog_INFO(TAG, "%s Loaded %s backend for rdpsnd", rdpsnd_is_dyn_str(rdpsnd->dynamic), name);
936
937 return error;
938}
939
940WINPR_ATTR_NODISCARD
941static BOOL rdpsnd_set_subsystem(rdpsndPlugin* rdpsnd, const char* subsystem)
942{
943 free(rdpsnd->subsystem);
944 rdpsnd->subsystem = _strdup(subsystem);
945 return (rdpsnd->subsystem != nullptr);
946}
947
948WINPR_ATTR_NODISCARD
949static BOOL rdpsnd_set_device_name(rdpsndPlugin* rdpsnd, const char* device_name)
950{
951 free(rdpsnd->device_name);
952 rdpsnd->device_name = _strdup(device_name);
953 return (rdpsnd->device_name != nullptr);
954}
955
956/* format:<format> accepts a numeric wFormatTag (e.g. 0x704F) or the name
957 * audio_format_get_tag_string() returns for it, with or without the
958 * WAVE_FORMAT_ prefix (e.g. opus, alaw, aac_ms).
959 * Anything else is rejected instead of silently becoming WAVE_FORMAT_UNKNOWN,
960 * which would allow any format. */
961WINPR_ATTR_NODISCARD
962static BOOL rdpsnd_parse_format_tag(const char* value, UINT16* tag)
963{
964 static const char prefix[] = "WAVE_FORMAT_";
965 const size_t prefixlen = strnlen(prefix, sizeof(prefix));
966
967 WINPR_ASSERT(value);
968 WINPR_ASSERT(tag);
969
970 char* end = nullptr;
971 errno = 0;
972 const unsigned long val = strtoul(value, &end, 0);
973 if ((errno == 0) && (end != value) && (*end == '\0'))
974 {
975 if (val > UINT16_MAX)
976 return FALSE;
977 *tag = (UINT16)val;
978 return TRUE;
979 }
980
981 const char* name = value;
982 if (_strnicmp(name, prefix, prefixlen) == 0)
983 name += prefixlen;
984
985 for (UINT32 x = 0; x <= UINT16_MAX; x++)
986 {
987 const char* str = audio_format_get_tag_string((UINT16)x);
988 if (strncmp(str, prefix, prefixlen) == 0)
989 str += prefixlen;
990 if (_stricmp(name, str) == 0)
991 {
992 *tag = (UINT16)x;
993 return TRUE;
994 }
995 }
996
997 return FALSE;
998}
999
1005WINPR_ATTR_NODISCARD
1006static UINT rdpsnd_process_addin_args(rdpsndPlugin* rdpsnd, const ADDIN_ARGV* args)
1007{
1008 int status = 0;
1009 DWORD flags = 0;
1010 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
1011 COMMAND_LINE_ARGUMENT_A rdpsnd_args[] = {
1012 { "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", nullptr, nullptr, -1, nullptr,
1013 "subsystem" },
1014 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", nullptr, nullptr, -1, nullptr, "device" },
1015 { "format", COMMAND_LINE_VALUE_REQUIRED, "<format>", nullptr, nullptr, -1, nullptr,
1016 "format" },
1017 { "rate", COMMAND_LINE_VALUE_REQUIRED, "<rate>", nullptr, nullptr, -1, nullptr, "rate" },
1018 { "channel", COMMAND_LINE_VALUE_REQUIRED, "<channel>", nullptr, nullptr, -1, nullptr,
1019 "channel" },
1020 { "latency", COMMAND_LINE_VALUE_REQUIRED, "<latency>", nullptr, nullptr, -1, nullptr,
1021 "latency" },
1022 { "quality", COMMAND_LINE_VALUE_REQUIRED, "<quality mode>", nullptr, nullptr, -1, nullptr,
1023 "quality mode" },
1024 { nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
1025 };
1026 rdpsnd->wQualityMode = HIGH_QUALITY; /* default quality mode */
1027
1028 if (args->argc > 1)
1029 {
1030 flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
1031 status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_args, flags, rdpsnd,
1032 nullptr, nullptr);
1033
1034 if (status < 0)
1035 return CHANNEL_RC_INITIALIZATION_ERROR;
1036
1037 arg = rdpsnd_args;
1038 errno = 0;
1039
1040 do
1041 {
1042 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
1043 continue;
1044
1045 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "sys")
1046 {
1047 if (!rdpsnd_set_subsystem(rdpsnd, arg->Value))
1048 return CHANNEL_RC_NO_MEMORY;
1049 }
1050 CommandLineSwitchCase(arg, "dev")
1051 {
1052 if (!rdpsnd_set_device_name(rdpsnd, arg->Value))
1053 return CHANNEL_RC_NO_MEMORY;
1054 }
1055 CommandLineSwitchCase(arg, "format")
1056 {
1057 UINT16 tag = WAVE_FORMAT_UNKNOWN;
1058
1059 if (!rdpsnd_parse_format_tag(arg->Value, &tag))
1060 {
1061 WLog_Print(rdpsnd->log, WLOG_ERROR,
1062 "invalid format '%s', expected a format tag (e.g. 0x704F) or "
1063 "a WAVE_FORMAT_ name (e.g. opus, pcm, alaw, aac_ms)",
1064 arg->Value);
1065 return CHANNEL_RC_INITIALIZATION_ERROR;
1066 }
1067
1068 rdpsnd->fixed_format->wFormatTag = tag;
1069 }
1070 CommandLineSwitchCase(arg, "rate")
1071 {
1072 unsigned long val = strtoul(arg->Value, nullptr, 0);
1073
1074 if ((errno != 0) || (val > UINT32_MAX))
1075 return CHANNEL_RC_INITIALIZATION_ERROR;
1076
1077 rdpsnd->fixed_format->nSamplesPerSec = (UINT32)val;
1078 }
1079 CommandLineSwitchCase(arg, "channel")
1080 {
1081 unsigned long val = strtoul(arg->Value, nullptr, 0);
1082
1083 if ((errno != 0) || (val > UINT16_MAX))
1084 return CHANNEL_RC_INITIALIZATION_ERROR;
1085
1086 rdpsnd->fixed_format->nChannels = (UINT16)val;
1087 }
1088 CommandLineSwitchCase(arg, "latency")
1089 {
1090 unsigned long val = strtoul(arg->Value, nullptr, 0);
1091
1092 if ((errno != 0) || (val > UINT32_MAX))
1093 return CHANNEL_RC_INITIALIZATION_ERROR;
1094
1095 rdpsnd->latency = (UINT32)val;
1096 }
1097 CommandLineSwitchCase(arg, "quality")
1098 {
1099 long wQualityMode = DYNAMIC_QUALITY;
1100
1101 if (_stricmp(arg->Value, "dynamic") == 0)
1102 wQualityMode = DYNAMIC_QUALITY;
1103 else if (_stricmp(arg->Value, "medium") == 0)
1104 wQualityMode = MEDIUM_QUALITY;
1105 else if (_stricmp(arg->Value, "high") == 0)
1106 wQualityMode = HIGH_QUALITY;
1107 else
1108 {
1109 wQualityMode = strtol(arg->Value, nullptr, 0);
1110
1111 if (errno != 0)
1112 return CHANNEL_RC_INITIALIZATION_ERROR;
1113 }
1114
1115 if ((wQualityMode < 0) || (wQualityMode > 2))
1116 wQualityMode = DYNAMIC_QUALITY;
1117
1118 rdpsnd->wQualityMode = (UINT16)wQualityMode;
1119 }
1120 CommandLineSwitchDefault(arg)
1121 {
1122 }
1123 CommandLineSwitchEnd(arg)
1124 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
1125 }
1126
1127 return CHANNEL_RC_OK;
1128}
1129
1135WINPR_ATTR_NODISCARD
1136static UINT rdpsnd_process_connect(rdpsndPlugin* rdpsnd)
1137{
1138 const struct
1139 {
1140 const char* subsystem;
1141 const char* device;
1142 } backends[] = {
1143#if defined(WITH_IOSAUDIO)
1144 { "ios", "" },
1145#endif
1146#if defined(WITH_AAUDIO)
1147 { "aaudio", "" },
1148#endif
1149#if defined(WITH_OPENSLES)
1150 { "opensles", "" },
1151#endif
1152#if defined(WITH_PULSE)
1153 { "pulse", "" },
1154#endif
1155#if defined(WITH_ALSA)
1156 { "alsa", "default" },
1157#endif
1158#if defined(WITH_OSS)
1159 { "oss", "" },
1160#endif
1161#if defined(WITH_MACAUDIO)
1162 { "mac", "default" },
1163#endif
1164#if defined(WITH_WINMM)
1165 { "winmm", "" },
1166#endif
1167#if defined(WITH_SNDIO)
1168 { "sndio", "" },
1169#endif
1170#if defined(__EMSCRIPTEN__)
1171 { "emscripten", "" },
1172#endif
1173#if defined(WITH_SDL_AUDIO)
1174 { "sdl", "" },
1175#endif
1176 { "fake", "" }
1177 };
1178 const ADDIN_ARGV* args = nullptr;
1179 UINT status = ERROR_INTERNAL_ERROR;
1180 WINPR_ASSERT(rdpsnd);
1181 rdpsnd->latency = 0;
1182 args = (const ADDIN_ARGV*)rdpsnd->channelEntryPoints.pExtendedData;
1183
1184 if (args)
1185 {
1186 status = rdpsnd_process_addin_args(rdpsnd, args);
1187
1188 if (status != CHANNEL_RC_OK)
1189 return status;
1190 }
1191
1192 if (rdpsnd->subsystem)
1193 {
1194 if ((status = rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args)))
1195 {
1196 WLog_ERR(TAG,
1197 "%s Unable to load sound playback subsystem %s because of error %" PRIu32 "",
1198 rdpsnd_is_dyn_str(rdpsnd->dynamic), rdpsnd->subsystem, status);
1199 return status;
1200 }
1201 }
1202 else
1203 {
1204 for (size_t x = 0; x < ARRAYSIZE(backends); x++)
1205 {
1206 const char* subsystem_name = backends[x].subsystem;
1207 const char* device_name = backends[x].device;
1208
1209 if ((status = rdpsnd_load_device_plugin(rdpsnd, subsystem_name, args)))
1210 WLog_WARN(TAG,
1211 "%s Unable to load sound playback subsystem %s because of error %" PRIu32
1212 "",
1213 rdpsnd_is_dyn_str(rdpsnd->dynamic), subsystem_name, status);
1214
1215 if (!rdpsnd->device)
1216 continue;
1217
1218 if (!rdpsnd_set_subsystem(rdpsnd, subsystem_name) ||
1219 !rdpsnd_set_device_name(rdpsnd, device_name))
1220 return CHANNEL_RC_NO_MEMORY;
1221
1222 break;
1223 }
1224
1225 if (!rdpsnd->device || status)
1226 return CHANNEL_RC_INITIALIZATION_ERROR;
1227 }
1228
1229 return CHANNEL_RC_OK;
1230}
1231
1237UINT rdpsnd_virtual_channel_write(rdpsndPlugin* rdpsnd, wStream* s)
1238{
1239 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1240
1241 if (rdpsnd)
1242 {
1243 if (rdpsnd->dynamic)
1244 {
1245 if (rdpsnd->listener_callback && rdpsnd->connected)
1246 {
1247 IWTSVirtualChannel* channel = rdpsnd->listener_callback->channel_callback->channel;
1248 status =
1249 channel->Write(channel, (UINT32)Stream_Length(s), Stream_Buffer(s), nullptr);
1250 }
1251 Stream_Free(s, TRUE);
1252 }
1253 else
1254 {
1255 status = rdpsnd->channelEntryPoints.pVirtualChannelWriteEx(
1256 rdpsnd->InitHandle, rdpsnd->OpenHandle, Stream_Buffer(s),
1257 (UINT32)Stream_GetPosition(s), s);
1258
1259 if (status != CHANNEL_RC_OK)
1260 {
1261 Stream_Free(s, TRUE);
1262 WLog_ERR(TAG, "%s pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1263 rdpsnd_is_dyn_str(FALSE), WTSErrorToString(status), status);
1264 }
1265 }
1266 }
1267
1268 return status;
1269}
1270
1276WINPR_ATTR_NODISCARD
1277static UINT rdpsnd_virtual_channel_event_data_received(rdpsndPlugin* plugin, void* pData,
1278 UINT32 dataLength, UINT32 totalLength,
1279 UINT32 dataFlags)
1280{
1281 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1282 return CHANNEL_RC_OK;
1283
1284 if (dataFlags & CHANNEL_FLAG_FIRST)
1285 {
1286 if (plugin->firstFlagReceived)
1287 return ERROR_INVALID_DATA;
1288 plugin->firstFlagReceived = TRUE;
1289
1290 if (!plugin->data_in)
1291 plugin->data_in = StreamPool_Take(plugin->pool, dataLength);
1292
1293 Stream_ResetPosition(plugin->data_in);
1294 plugin->totalLength = totalLength;
1295 }
1296
1297 if (!plugin->data_in)
1298 return ERROR_INVALID_DATA;
1299
1300 if (!Stream_EnsureRemainingCapacity(plugin->data_in, dataLength))
1301 return CHANNEL_RC_NO_MEMORY;
1302
1303 Stream_Write(plugin->data_in, pData, dataLength);
1304
1305 if ((Stream_GetPosition(plugin->data_in) > totalLength) || (totalLength != plugin->totalLength))
1306 return ERROR_INVALID_DATA;
1307
1308 if (dataFlags & CHANNEL_FLAG_LAST)
1309 {
1310 if (!plugin->firstFlagReceived)
1311 return ERROR_INVALID_DATA;
1312 if (Stream_GetPosition(plugin->data_in) != totalLength)
1313 return ERROR_INVALID_DATA;
1314 plugin->firstFlagReceived = FALSE;
1315
1316 Stream_SealLength(plugin->data_in);
1317 Stream_ResetPosition(plugin->data_in);
1318 plugin->totalLength = 0;
1319
1320 if (plugin->async)
1321 {
1322 if (!MessageQueue_Post(plugin->queue, nullptr, 0, plugin->data_in, nullptr))
1323 return ERROR_INTERNAL_ERROR;
1324 plugin->data_in = nullptr;
1325 }
1326 else
1327 {
1328 UINT error = rdpsnd_recv_pdu(plugin, plugin->data_in);
1329 plugin->data_in = nullptr;
1330 if (error)
1331 return error;
1332 }
1333 }
1334
1335 return CHANNEL_RC_OK;
1336}
1337
1338static VOID VCAPITYPE rdpsnd_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1339 UINT event, LPVOID pData,
1340 UINT32 dataLength, UINT32 totalLength,
1341 UINT32 dataFlags)
1342{
1343 UINT error = CHANNEL_RC_OK;
1344 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)lpUserParam;
1345 WINPR_ASSERT(rdpsnd);
1346 WINPR_ASSERT(!rdpsnd->dynamic);
1347
1348 switch (event)
1349 {
1350 case CHANNEL_EVENT_DATA_RECEIVED:
1351 if (!rdpsnd)
1352 return;
1353
1354 if (rdpsnd->OpenHandle != openHandle)
1355 {
1356 WLog_ERR(TAG, "%s error no match", rdpsnd_is_dyn_str(rdpsnd->dynamic));
1357 return;
1358 }
1359 if ((error = rdpsnd_virtual_channel_event_data_received(rdpsnd, pData, dataLength,
1360 totalLength, dataFlags)))
1361 WLog_ERR(TAG,
1362 "%s rdpsnd_virtual_channel_event_data_received failed with error %" PRIu32
1363 "",
1364 rdpsnd_is_dyn_str(rdpsnd->dynamic), error);
1365
1366 break;
1367
1368 case CHANNEL_EVENT_WRITE_CANCELLED:
1369 case CHANNEL_EVENT_WRITE_COMPLETE:
1370 {
1371 wStream* s = (wStream*)pData;
1372 Stream_Free(s, TRUE);
1373 }
1374 break;
1375
1376 case CHANNEL_EVENT_USER:
1377 break;
1378 default:
1379 break;
1380 }
1381
1382 if (error && rdpsnd && rdpsnd->rdpcontext)
1383 {
1384 char buffer[8192];
1385 (void)_snprintf(buffer, sizeof(buffer),
1386 "%s rdpsnd_virtual_channel_open_event_ex reported an error",
1387 rdpsnd_is_dyn_str(rdpsnd->dynamic));
1388 setChannelError(rdpsnd->rdpcontext, error, buffer);
1389 }
1390}
1391
1397WINPR_ATTR_NODISCARD
1398static UINT rdpsnd_virtual_channel_event_connected(rdpsndPlugin* rdpsnd, LPVOID pData,
1399 UINT32 dataLength)
1400{
1401 UINT32 status = 0;
1402 DWORD opened = 0;
1403 WINPR_UNUSED(pData);
1404 WINPR_UNUSED(dataLength);
1405
1406 WINPR_ASSERT(rdpsnd);
1407 WINPR_ASSERT(!rdpsnd->dynamic);
1408
1409 status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx(
1410 rdpsnd->InitHandle, &opened, rdpsnd->channelDef.name, rdpsnd_virtual_channel_open_event_ex);
1411
1412 if (status != CHANNEL_RC_OK)
1413 {
1414 WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]",
1415 rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status);
1416 goto fail;
1417 }
1418
1419 if (rdpsnd_process_connect(rdpsnd) != CHANNEL_RC_OK)
1420 goto fail;
1421
1422 rdpsnd->OpenHandle = opened;
1423 return CHANNEL_RC_OK;
1424fail:
1425 if (opened != 0)
1426 rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
1427 return CHANNEL_RC_NO_MEMORY;
1428}
1429
1430static void rdpsnd_terminate_thread(rdpsndPlugin* rdpsnd)
1431{
1432 WINPR_ASSERT(rdpsnd);
1433 if (rdpsnd->async)
1434 {
1435 if (rdpsnd->queue)
1436 MessageQueue_PostQuit(rdpsnd->queue, 0);
1437
1438 if (rdpsnd->thread)
1439 {
1440 (void)WaitForSingleObject(rdpsnd->thread, INFINITE);
1441 (void)CloseHandle(rdpsnd->thread);
1442 }
1443 DeleteCriticalSection(&rdpsnd->asyncLock);
1444 MessageQueue_Free(rdpsnd->queue);
1445 }
1446 rdpsnd->thread = nullptr;
1447 rdpsnd->queue = nullptr;
1448}
1449
1450static void cleanup_internals(rdpsndPlugin* rdpsnd)
1451{
1452 if (!rdpsnd)
1453 return;
1454
1455 if (rdpsnd->async)
1456 {
1457 EnterCriticalSection(&rdpsnd->asyncLock);
1458 if (rdpsnd->queue && (MessageQueue_Clear(rdpsnd->queue) < 0))
1459 WLog_WARN(TAG, "%s failed to clear the message queue",
1460 rdpsnd_is_dyn_str(rdpsnd->dynamic));
1461 }
1462
1463 if (rdpsnd->device)
1464 {
1465 IFCALL(rdpsnd->device->Close, rdpsnd->device);
1466 IFCALL(rdpsnd->device->Free, rdpsnd->device);
1467 rdpsnd->device = nullptr;
1468 }
1469 rdpsnd->isOpen = FALSE;
1470 rdpsnd->expectingWave = FALSE;
1471 rdpsnd->firstFlagReceived = FALSE;
1472 rdpsnd->totalLength = 0;
1473
1474 if (rdpsnd->pool)
1475 StreamPool_Return(rdpsnd->pool, rdpsnd->data_in);
1476
1477 audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats);
1478 audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
1479
1480 rdpsnd->NumberOfClientFormats = 0;
1481 rdpsnd->ClientFormats = nullptr;
1482 rdpsnd->NumberOfServerFormats = 0;
1483 rdpsnd->ServerFormats = nullptr;
1484
1485 rdpsnd->data_in = nullptr;
1486
1487 if (rdpsnd->async)
1488 LeaveCriticalSection(&rdpsnd->asyncLock);
1489}
1490
1496WINPR_ATTR_NODISCARD
1497static UINT rdpsnd_virtual_channel_event_disconnected(rdpsndPlugin* rdpsnd)
1498{
1499 UINT error = 0;
1500
1501 WINPR_ASSERT(rdpsnd);
1502 WINPR_ASSERT(!rdpsnd->dynamic);
1503 if (rdpsnd->OpenHandle != 0)
1504 {
1505 DWORD opened = rdpsnd->OpenHandle;
1506 rdpsnd->OpenHandle = 0;
1507 error = rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened);
1508
1509 if (CHANNEL_RC_OK != error)
1510 {
1511 WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]",
1512 rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error);
1513 }
1514 }
1515
1516 cleanup_internals(rdpsnd);
1517 return error;
1518}
1519
1520static void queue_free(void* obj)
1521{
1522 wMessage* msg = obj;
1523 if (!msg)
1524 return;
1525 if (msg->id != 0)
1526 return;
1527 wStream* s = msg->wParam;
1528 Stream_Release(s);
1529}
1530
1531static void free_internals(rdpsndPlugin* rdpsnd)
1532{
1533 if (!rdpsnd)
1534 return;
1535
1536 if (rdpsnd->references > 0)
1537 rdpsnd->references--;
1538
1539 if (rdpsnd->references > 0)
1540 return;
1541
1542 rdpsnd_terminate_thread(rdpsnd);
1543 freerdp_dsp_context_free(rdpsnd->dsp_context);
1544 StreamPool_Free(rdpsnd->pool);
1545 rdpsnd->pool = nullptr;
1546 rdpsnd->dsp_context = nullptr;
1547}
1548
1549WINPR_ATTR_NODISCARD
1550static BOOL allocate_internals(rdpsndPlugin* rdpsnd)
1551{
1552 WINPR_ASSERT(rdpsnd);
1553
1554 if (!rdpsnd->pool)
1555 {
1556 rdpsnd->pool = StreamPool_New(TRUE, 4096);
1557 if (!rdpsnd->pool)
1558 return FALSE;
1559 }
1560
1561 if (!rdpsnd->dsp_context)
1562 {
1563 rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE);
1564 if (!rdpsnd->dsp_context)
1565 return FALSE;
1566 }
1567
1568 if (rdpsnd->async)
1569 {
1570 if (!rdpsnd->queue)
1571 {
1572 wObject obj = WINPR_C_ARRAY_INIT;
1573
1574 obj.fnObjectFree = queue_free;
1575 rdpsnd->queue = MessageQueue_New(&obj);
1576 if (!rdpsnd->queue)
1577 return CHANNEL_RC_NO_MEMORY;
1578 InitializeCriticalSection(&rdpsnd->asyncLock);
1579 }
1580
1581 if (!rdpsnd->thread)
1582 {
1583 rdpsnd->thread = CreateThread(nullptr, 0, play_thread, rdpsnd, 0, nullptr);
1584 if (!rdpsnd->thread)
1585 return CHANNEL_RC_INITIALIZATION_ERROR;
1586 }
1587 }
1588
1589 rdpsnd->references++;
1590
1591 return TRUE;
1592}
1593
1594WINPR_ATTR_NODISCARD
1595static DWORD WINAPI play_thread(LPVOID arg)
1596{
1597 UINT error = CHANNEL_RC_OK;
1598 rdpsndPlugin* rdpsnd = arg;
1599
1600 if (!rdpsnd || !rdpsnd->queue)
1601 return ERROR_INVALID_PARAMETER;
1602
1603 while (TRUE)
1604 {
1605 int rc = -1;
1606 wMessage message = WINPR_C_ARRAY_INIT;
1607 wStream* s = nullptr;
1608 DWORD status = 0;
1609 DWORD nCount = 0;
1610 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
1611
1612 handles[nCount++] = MessageQueue_Event(rdpsnd->queue);
1613 status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
1614 switch (status)
1615 {
1616 case WAIT_OBJECT_0:
1617 break;
1618 default:
1619 return ERROR_TIMEOUT;
1620 }
1621
1622 /* cleanup_internals clears the queue under this lock. */
1623 if (rdpsnd->async)
1624 EnterCriticalSection(&rdpsnd->asyncLock);
1625
1626 rc = MessageQueue_Peek(rdpsnd->queue, &message, TRUE);
1627 if ((rc > 0) && (message.id != WMQ_QUIT))
1628 {
1629 s = message.wParam;
1630 error = rdpsnd_recv_pdu(rdpsnd, s);
1631 }
1632
1633 if (rdpsnd->async)
1634 LeaveCriticalSection(&rdpsnd->asyncLock);
1635
1636 if (rc < 1)
1637 continue;
1638
1639 if (message.id == WMQ_QUIT)
1640 break;
1641
1642 if (error)
1643 WLog_Print(rdpsnd->log, WLOG_WARN, "%s PDU dropped [%08" PRIX32 "]",
1644 rdpsnd_is_dyn_str(rdpsnd->dynamic), error);
1645 error = CHANNEL_RC_OK;
1646 }
1647
1648 return CHANNEL_RC_OK;
1649}
1650
1651WINPR_ATTR_NODISCARD
1652static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd)
1653{
1654 if (!rdpsnd)
1655 return ERROR_INVALID_PARAMETER;
1656
1657 if (!allocate_internals(rdpsnd))
1658 return CHANNEL_RC_NO_MEMORY;
1659
1660 return CHANNEL_RC_OK;
1661}
1662
1663void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
1664{
1665 if (rdpsnd)
1666 {
1667 free_internals(rdpsnd);
1668 audio_formats_free(rdpsnd->fixed_format, 1);
1669 free(rdpsnd->subsystem);
1670 free(rdpsnd->device_name);
1671 rdpsnd->InitHandle = nullptr;
1672 }
1673
1674 free(rdpsnd);
1675}
1676
1677static VOID VCAPITYPE rdpsnd_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
1678 UINT event, LPVOID pData,
1679 UINT dataLength)
1680{
1681 UINT error = CHANNEL_RC_OK;
1682 rdpsndPlugin* plugin = (rdpsndPlugin*)lpUserParam;
1683
1684 if (!plugin)
1685 return;
1686
1687 if (plugin->InitHandle != pInitHandle)
1688 {
1689 WLog_ERR(TAG, "%s error no match", rdpsnd_is_dyn_str(plugin->dynamic));
1690 return;
1691 }
1692
1693 switch (event)
1694 {
1695 case CHANNEL_EVENT_INITIALIZED:
1696 error = rdpsnd_virtual_channel_event_initialized(plugin);
1697 break;
1698
1699 case CHANNEL_EVENT_CONNECTED:
1700 error = rdpsnd_virtual_channel_event_connected(plugin, pData, dataLength);
1701 break;
1702
1703 case CHANNEL_EVENT_DISCONNECTED:
1704 error = rdpsnd_virtual_channel_event_disconnected(plugin);
1705 break;
1706
1707 case CHANNEL_EVENT_TERMINATED:
1708 rdpsnd_virtual_channel_event_terminated(plugin);
1709 plugin = nullptr;
1710 break;
1711
1712 case CHANNEL_EVENT_ATTACHED:
1713 plugin->attached = TRUE;
1714 break;
1715
1716 case CHANNEL_EVENT_DETACHED:
1717 plugin->attached = FALSE;
1718 break;
1719
1720 default:
1721 break;
1722 }
1723
1724 if (error && plugin && plugin->rdpcontext)
1725 {
1726 char buffer[8192];
1727 (void)_snprintf(buffer, sizeof(buffer), "%s reported an error",
1728 rdpsnd_is_dyn_str(plugin->dynamic));
1729 setChannelError(plugin->rdpcontext, error, buffer);
1730 }
1731}
1732
1733rdpContext* freerdp_rdpsnd_get_context(rdpsndPlugin* plugin)
1734{
1735 if (!plugin)
1736 return nullptr;
1737
1738 return plugin->rdpcontext;
1739}
1740
1741WINPR_ATTR_NODISCARD
1742static rdpsndPlugin* allocatePlugin(void)
1743{
1744 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin));
1745 if (!rdpsnd)
1746 goto fail;
1747
1748 rdpsnd->fixed_format = audio_format_new();
1749 if (!rdpsnd->fixed_format)
1750 goto fail;
1751 rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client");
1752 if (!rdpsnd->log)
1753 goto fail;
1754
1755 rdpsnd->attached = TRUE;
1756 return rdpsnd;
1757
1758fail:
1759 if (rdpsnd)
1760 audio_formats_free(rdpsnd->fixed_format, 1);
1761 free(rdpsnd);
1762 return nullptr;
1763}
1764/* rdpsnd is always built-in */
1765FREERDP_ENTRY_POINT(BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(
1766 PCHANNEL_ENTRY_POINTS_EX pEntryPoints, PVOID pInitHandle))
1767{
1768 UINT rc = 0;
1769 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
1770
1771 if (!pEntryPoints)
1772 return FALSE;
1773
1774 rdpsndPlugin* rdpsnd = allocatePlugin();
1775
1776 if (!rdpsnd)
1777 return FALSE;
1778
1779 rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP;
1780 (void)sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name),
1781 RDPSND_CHANNEL_NAME);
1782 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
1783
1784 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
1785 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
1786 {
1787 rdpsnd->rdpcontext = pEntryPointsEx->context;
1788 if (!freerdp_settings_get_bool(rdpsnd->rdpcontext->settings,
1789 FreeRDP_SynchronousStaticChannels))
1790 rdpsnd->async = TRUE;
1791 }
1792
1793 CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints,
1795 rdpsnd->InitHandle = pInitHandle;
1796
1797 WINPR_ASSERT(rdpsnd->channelEntryPoints.pVirtualChannelInitEx);
1798 rc = rdpsnd->channelEntryPoints.pVirtualChannelInitEx(
1799 rdpsnd, nullptr, pInitHandle, &rdpsnd->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
1800 rdpsnd_virtual_channel_init_event_ex);
1801
1802 if (CHANNEL_RC_OK != rc)
1803 {
1804 WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]",
1805 rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc);
1806 rdpsnd_virtual_channel_event_terminated(rdpsnd);
1807 return FALSE;
1808 }
1809
1810 return TRUE;
1811}
1812
1813WINPR_ATTR_NODISCARD
1814static UINT rdpsnd_on_open(IWTSVirtualChannelCallback* pChannelCallback)
1815{
1816 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
1817 rdpsndPlugin* rdpsnd = nullptr;
1818
1819 WINPR_ASSERT(callback);
1820
1821 rdpsnd = (rdpsndPlugin*)callback->plugin;
1822 WINPR_ASSERT(rdpsnd);
1823
1824 if (rdpsnd->OnOpenCalled)
1825 return CHANNEL_RC_OK;
1826 rdpsnd->OnOpenCalled = TRUE;
1827
1828 if (!allocate_internals(rdpsnd))
1829 return ERROR_OUTOFMEMORY;
1830
1831 return rdpsnd_process_connect(rdpsnd);
1832}
1833
1834WINPR_ATTR_NODISCARD
1835static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
1836{
1837 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
1838 rdpsndPlugin* plugin = nullptr;
1839 wStream* copy = nullptr;
1840 size_t len = 0;
1841
1842 len = Stream_GetRemainingLength(data);
1843
1844 if (!callback || !callback->plugin)
1845 return ERROR_INVALID_PARAMETER;
1846 plugin = (rdpsndPlugin*)callback->plugin;
1847 WINPR_ASSERT(plugin);
1848
1849 copy = StreamPool_Take(plugin->pool, len);
1850 if (!copy)
1851 return ERROR_OUTOFMEMORY;
1852 Stream_Copy(data, copy, len);
1853 Stream_SealLength(copy);
1854 Stream_ResetPosition(copy);
1855
1856 if (plugin->async)
1857 {
1858 if (!MessageQueue_Post(plugin->queue, nullptr, 0, copy, nullptr))
1859 {
1860 Stream_Release(copy);
1861 return ERROR_INTERNAL_ERROR;
1862 }
1863 }
1864 else
1865 {
1866 UINT error = rdpsnd_recv_pdu(plugin, copy);
1867 if (error)
1868 return error;
1869 }
1870
1871 return CHANNEL_RC_OK;
1872}
1873
1874WINPR_ATTR_NODISCARD
1875static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1876{
1877 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
1878
1879 WINPR_ASSERT(callback);
1880
1881 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin;
1882 WINPR_ASSERT(rdpsnd);
1883
1884 rdpsnd->OnOpenCalled = FALSE;
1885 cleanup_internals(rdpsnd);
1886 free_internals(rdpsnd);
1887
1888 free(pChannelCallback);
1889 rdpsnd->connected = FALSE;
1890 return CHANNEL_RC_OK;
1891}
1892
1893// NOLINTBEGIN(readability-non-const-parameter)
1894WINPR_ATTR_NODISCARD
1895static UINT rdpsnd_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
1896 IWTSVirtualChannel* pChannel, BYTE* Data,
1897 BOOL* pbAccept,
1898 IWTSVirtualChannelCallback** ppCallback)
1899// NOLINTEND(readability-non-const-parameter)
1900{
1901 GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
1902 WINPR_ASSERT(listener_callback);
1903 WINPR_ASSERT(pChannel);
1904 WINPR_ASSERT(ppCallback);
1905
1906 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)listener_callback->plugin;
1907 WINPR_ASSERT(rdpsnd);
1908
1909 if (rdpsnd->connected)
1910 {
1911 WLog_Print(rdpsnd->log, WLOG_ERROR, "Channel already connected, terminating.");
1912 return ERROR_DEVICE_ALREADY_ATTACHED;
1913 }
1914
1915 GENERIC_CHANNEL_CALLBACK* callback =
1917
1918 WINPR_UNUSED(Data);
1919 WINPR_UNUSED(pbAccept);
1920
1921 if (!callback)
1922 {
1923 WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
1924 return CHANNEL_RC_NO_MEMORY;
1925 }
1926
1927 callback->iface.OnOpen = rdpsnd_on_open;
1928 callback->iface.OnDataReceived = rdpsnd_on_data_received;
1929 callback->iface.OnClose = rdpsnd_on_close;
1930 callback->plugin = listener_callback->plugin;
1931 callback->channel_mgr = listener_callback->channel_mgr;
1932 callback->channel = pChannel;
1933 listener_callback->channel_callback = callback;
1934 *ppCallback = &callback->iface;
1935 rdpsnd->connected = TRUE;
1936 return CHANNEL_RC_OK;
1937}
1938
1939WINPR_ATTR_NODISCARD
1940static UINT rdpsnd_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
1941{
1942 UINT status = 0;
1943 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)pPlugin;
1944 WINPR_ASSERT(rdpsnd);
1945 WINPR_ASSERT(pChannelMgr);
1946 if (rdpsnd->initialized)
1947 {
1948 WLog_ERR(TAG, "[%s] channel initialized twice, aborting", RDPSND_DVC_CHANNEL_NAME);
1949 return ERROR_INVALID_DATA;
1950 }
1951 rdpsnd->listener_callback =
1953
1954 if (!rdpsnd->listener_callback)
1955 {
1956 WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
1957 return CHANNEL_RC_NO_MEMORY;
1958 }
1959
1960 rdpsnd->listener_callback->iface.OnNewChannelConnection = rdpsnd_on_new_channel_connection;
1961 rdpsnd->listener_callback->plugin = pPlugin;
1962 rdpsnd->listener_callback->channel_mgr = pChannelMgr;
1963 status = pChannelMgr->CreateListener(pChannelMgr, RDPSND_DVC_CHANNEL_NAME, 0,
1964 &rdpsnd->listener_callback->iface, &(rdpsnd->listener));
1965 if (status != CHANNEL_RC_OK)
1966 {
1967 WLog_ERR(TAG, "%s CreateListener failed!", rdpsnd_is_dyn_str(TRUE));
1968 return status;
1969 }
1970
1971 rdpsnd->listener->pInterface = rdpsnd->iface.pInterface;
1972 status = rdpsnd_virtual_channel_event_initialized(rdpsnd);
1973
1974 rdpsnd->initialized = status == CHANNEL_RC_OK;
1975 return status;
1976}
1977
1983WINPR_ATTR_NODISCARD
1984static UINT rdpsnd_plugin_terminated(IWTSPlugin* pPlugin)
1985{
1986 rdpsndPlugin* rdpsnd = (rdpsndPlugin*)pPlugin;
1987 if (rdpsnd)
1988 {
1989 if (rdpsnd->listener_callback)
1990 {
1991 IWTSVirtualChannelManager* mgr = rdpsnd->listener_callback->channel_mgr;
1992 if (mgr)
1993 IFCALL(mgr->DestroyListener, mgr, rdpsnd->listener);
1994 }
1995 free(rdpsnd->listener_callback);
1996 free(rdpsnd->iface.pInterface);
1997 }
1998 rdpsnd_virtual_channel_event_terminated(rdpsnd);
1999 return CHANNEL_RC_OK;
2000}
2001
2007FREERDP_ENTRY_POINT(UINT VCAPITYPE rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
2008{
2009 UINT error = CHANNEL_RC_OK;
2010
2011 WINPR_ASSERT(pEntryPoints);
2012 WINPR_ASSERT(pEntryPoints->GetPlugin);
2013
2014 rdpsndPlugin* rdpsnd =
2015 (rdpsndPlugin*)pEntryPoints->GetPlugin(pEntryPoints, RDPSND_CHANNEL_NAME);
2016
2017 if (!rdpsnd)
2018 {
2019 IWTSPlugin* iface = nullptr;
2020 union
2021 {
2022 const void* cev;
2023 void* ev;
2024 } cnv;
2025
2026 rdpsnd = allocatePlugin();
2027 if (!rdpsnd)
2028 {
2029 WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE));
2030 return CHANNEL_RC_NO_MEMORY;
2031 }
2032
2033 iface = &rdpsnd->iface;
2034 iface->Initialize = rdpsnd_plugin_initialize;
2035 iface->Connected = nullptr;
2036 iface->Disconnected = nullptr;
2037 iface->Terminated = rdpsnd_plugin_terminated;
2038
2039 rdpsnd->dynamic = TRUE;
2040
2041 WINPR_ASSERT(pEntryPoints->GetRdpContext);
2042 rdpsnd->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
2043
2044 if (!freerdp_settings_get_bool(rdpsnd->rdpcontext->settings,
2045 FreeRDP_SynchronousDynamicChannels))
2046 rdpsnd->async = TRUE;
2047
2048 /* user data pointer is not const, cast to avoid warning. */
2049 cnv.cev = pEntryPoints->GetPluginData(pEntryPoints);
2050 WINPR_ASSERT(pEntryPoints->GetPluginData);
2051 rdpsnd->channelEntryPoints.pExtendedData = cnv.ev;
2052
2053 error = pEntryPoints->RegisterPlugin(pEntryPoints, RDPSND_CHANNEL_NAME, iface);
2054 }
2055 else
2056 {
2057 WLog_ERR(TAG, "%s could not get rdpsnd Plugin.", rdpsnd_is_dyn_str(TRUE));
2058 return CHANNEL_RC_BAD_CHANNEL;
2059 }
2060
2061 return error;
2062}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition svc.h:60
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59