23#include <winpr/synch.h>
24#include <winpr/thread.h>
25#include <winpr/stream.h>
26#include <winpr/sysinfo.h>
28#include <freerdp/log.h>
29#include <freerdp/codec/audio.h>
30#include <freerdp/codec/dsp.h>
32#include "win_shadow.h"
34#include "win_wasapi.h"
36#define TAG SERVER_TAG("shadow.win")
42#define WIN_AUDIO_POLL_MS 10
53 { WAVE_FORMAT_OPUS, WIN_WASAPI_CHANNELS, WIN_WASAPI_RATE, 12000, 1, 16, 0,
nullptr },
54 { WAVE_FORMAT_PCM, WIN_WASAPI_CHANNELS, WIN_WASAPI_RATE, WIN_WASAPI_RATE * 4, 4, 16, 0,
61 WAVE_FORMAT_PCM, WIN_WASAPI_CHANNELS, WIN_WASAPI_RATE, WIN_WASAPI_RATE * 4, 4, 16, 0,
nullptr
64static void win_audio_msg_free(UINT32
id, SHADOW_MSG_OUT* msg)
75static void win_audio_post(winShadowSubsystem* subsystem,
wStream* s)
77 WINPR_ASSERT(subsystem);
79 const size_t size = Stream_GetPosition(s);
80 Stream_ResetPosition(s);
84 rdpShadowServer* server = subsystem->base.server;
85 if (!server || !server->clients || (ArrayList_Count(server->clients) == 0))
93 msg->buf = malloc(size);
100 memcpy(msg->buf, Stream_Buffer(s), size);
101 msg->common.Free = win_audio_msg_free;
102 msg->audio_format = &win_audio_source_format;
103 msg->nFrames = size / WIN_WASAPI_FRAME_BYTES;
104 msg->wTimestamp = (UINT16)(GetTickCount64() & 0xFFFF);
106 (void)shadow_client_boardcast_msg(server,
nullptr, SHADOW_MSG_OUT_AUDIO_OUT_SAMPLES_ID,
107 &msg->common,
nullptr);
110static DWORD WINAPI win_audio_thread(LPVOID arg)
112 winShadowSubsystem* subsystem = (winShadowSubsystem*)arg;
113 WINPR_ASSERT(subsystem);
115 const HRESULT hr = CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
118 WLog_ERR(TAG,
"audio: CoInitializeEx failed (0x%08lX): no sound", hr);
122 wStream* s = Stream_New(
nullptr, 1ull * WIN_WASAPI_RATE * WIN_WASAPI_FRAME_BYTES / 10);
125 while (s && (WaitForSingleObject(subsystem->audioStopEvent, 0) == WAIT_TIMEOUT))
127 winWasapiLoopback* loopback = win_wasapi_loopback_open(log);
133 (void)WaitForSingleObject(subsystem->audioStopEvent, 2000);
139 while (WaitForSingleObject(subsystem->audioStopEvent, WIN_AUDIO_POLL_MS) == WAIT_TIMEOUT)
141 const BOOL rc = win_wasapi_loopback_read(loopback, s);
142 win_audio_post(subsystem, s);
146 win_wasapi_loopback_close(loopback);
149 Stream_Free(s, TRUE);
154BOOL win_shadow_audio_init(winShadowSubsystem* subsystem)
156 WINPR_ASSERT(subsystem);
158 AUDIO_FORMAT* formats = audio_formats_new(ARRAYSIZE(win_audio_offered_formats));
163 for (
size_t x = 0; x < ARRAYSIZE(win_audio_offered_formats); x++)
165 const AUDIO_FORMAT* format = &win_audio_offered_formats[x];
166 if (freerdp_dsp_supports_format(format, TRUE))
167 formats[count++] = *format;
170 subsystem->base.rdpsndFormats = formats;
171 subsystem->base.nRdpsndFormats = count;
175void win_shadow_audio_uninit(winShadowSubsystem* subsystem)
177 WINPR_ASSERT(subsystem);
179 win_shadow_audio_stop(subsystem);
180 audio_formats_free(subsystem->base.rdpsndFormats, subsystem->base.nRdpsndFormats);
181 subsystem->base.rdpsndFormats =
nullptr;
182 subsystem->base.nRdpsndFormats = 0;
185BOOL win_shadow_audio_start(winShadowSubsystem* subsystem)
187 WINPR_ASSERT(subsystem);
189 if (subsystem->audioThread)
193 if (!subsystem->base.rdpsndFormats)
196 subsystem->audioStopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
197 if (!subsystem->audioStopEvent)
200 subsystem->audioThread = CreateThread(
nullptr, 0, win_audio_thread, subsystem, 0,
nullptr);
201 if (!subsystem->audioThread)
203 WLog_ERR(TAG,
"audio: failed to create the capture thread");
204 (void)CloseHandle(subsystem->audioStopEvent);
205 subsystem->audioStopEvent =
nullptr;
212void win_shadow_audio_stop(winShadowSubsystem* subsystem)
214 WINPR_ASSERT(subsystem);
216 if (!subsystem->audioThread)
219 (void)SetEvent(subsystem->audioStopEvent);
220 (void)WaitForSingleObject(subsystem->audioThread, INFINITE);
221 (void)CloseHandle(subsystem->audioThread);
222 (void)CloseHandle(subsystem->audioStopEvent);
223 subsystem->audioThread =
nullptr;
224 subsystem->audioStopEvent =
nullptr;