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