6 #include <mmdeviceapi.h>
7 #include <functiondiscoverykeys_devpkey.h>
8 #include <audioclient.h>
10 #include <freerdp/log.h>
11 #define TAG SERVER_TAG("windows")
16 #define REFTIMES_PER_SEC 100000
17 #define REFTIMES_PER_MILLISEC 100
23 DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92,
25 DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36,
27 DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03,
29 DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c,
34 wfPeerContext* latestPeer = NULL;
36 int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
42 int wf_wasapi_activate(RdpsndServerContext* context)
44 wchar_t* pattern = L
"Stereo Mix";
47 wf_wasapi_get_device_string(pattern, &devStr);
51 WLog_ERR(TAG,
"Failed to match for output device! Disabling rdpsnd.");
55 WLog_DBG(TAG,
"RDPSND (WASAPI) Activated");
56 if (!(hThread = CreateThread(NULL, 0, wf_rdpsnd_wasapi_thread, latestPeer, 0, NULL)))
58 WLog_ERR(TAG,
"CreateThread failed");
61 (void)CloseHandle(hThread);
66 int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr)
69 IMMDeviceEnumerator* pEnumerator = NULL;
70 IMMDeviceCollection* pCollection = NULL;
71 IMMDevice* pEndpoint = NULL;
72 IPropertyStore* pProps = NULL;
77 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
78 (
void**)&pEnumerator);
81 WLog_ERR(TAG,
"Failed to cocreate device enumerator");
85 hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eCapture, DEVICE_STATE_ACTIVE,
89 WLog_ERR(TAG,
"Failed to create endpoint collection");
93 pCollection->lpVtbl->GetCount(pCollection, &count);
94 WLog_INFO(TAG,
"Num endpoints: %u", count);
98 WLog_ERR(TAG,
"No endpoints!");
102 for (
unsigned int i = 0; i < count; ++i)
105 PropVariantInit(&nameVar);
107 hr = pCollection->lpVtbl->Item(pCollection, i, &pEndpoint);
110 WLog_ERR(TAG,
"Failed to get endpoint %u", i);
114 hr = pEndpoint->lpVtbl->GetId(pEndpoint, &pwszID);
117 WLog_ERR(TAG,
"Failed to get endpoint ID");
121 hr = pEndpoint->lpVtbl->OpenPropertyStore(pEndpoint, STGM_READ, &pProps);
124 WLog_ERR(TAG,
"Failed to open property store");
128 hr = pProps->lpVtbl->GetValue(pProps, &PKEY_Device_FriendlyName, &nameVar);
131 WLog_ERR(TAG,
"Failed to get device friendly name");
136 if (wcscmp(pattern, nameVar.pwszVal) < 0)
138 unsigned int devStrLen;
139 WLog_INFO(TAG,
"Using sound ouput endpoint: [%s] (%s)", nameVar.pwszVal, pwszID);
141 devStrLen = wcslen(pwszID);
142 *deviceStr = (LPWSTR)calloc(devStrLen + 1, 2);
145 wcscpy_s(*deviceStr, devStrLen + 1, pwszID);
147 CoTaskMemFree(pwszID);
149 PropVariantClear(&nameVar);
151 pProps->lpVtbl->Release(pProps);
154 pEndpoint->lpVtbl->Release(pEndpoint);
158 pCollection->lpVtbl->Release(pCollection);
161 pEnumerator->lpVtbl->Release(pEnumerator);
168 DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
170 IMMDeviceEnumerator* pEnumerator = NULL;
171 IMMDevice* pDevice = NULL;
172 IAudioClient* pAudioClient = NULL;
173 IAudioCaptureClient* pCaptureClient = NULL;
174 WAVEFORMATEX* pwfx = NULL;
176 REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
177 REFERENCE_TIME hnsActualDuration;
178 UINT32 bufferFrameCount;
179 UINT32 numFramesAvailable;
180 UINT32 packetLength = 0;
184 wfPeerContext* context;
187 wfi = wf_info_get_instance();
188 context = (wfPeerContext*)lpParam;
191 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
192 (
void**)&pEnumerator);
195 WLog_ERR(TAG,
"Failed to cocreate device enumerator");
199 hr = pEnumerator->lpVtbl->GetDevice(pEnumerator, devStr, &pDevice);
202 WLog_ERR(TAG,
"Failed to cocreate get device");
206 hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL,
207 (
void**)&pAudioClient);
210 WLog_ERR(TAG,
"Failed to activate audio client");
214 hr = pAudioClient->lpVtbl->GetMixFormat(pAudioClient, &pwfx);
217 WLog_ERR(TAG,
"Failed to get mix format");
221 pwfx->wFormatTag = wfi->agreed_format->wFormatTag;
222 pwfx->nChannels = wfi->agreed_format->nChannels;
223 pwfx->nSamplesPerSec = wfi->agreed_format->nSamplesPerSec;
224 pwfx->nAvgBytesPerSec = wfi->agreed_format->nAvgBytesPerSec;
225 pwfx->nBlockAlign = wfi->agreed_format->nBlockAlign;
226 pwfx->wBitsPerSample = wfi->agreed_format->wBitsPerSample;
227 pwfx->cbSize = wfi->agreed_format->cbSize;
229 hr = pAudioClient->lpVtbl->Initialize(pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0,
230 hnsRequestedDuration, 0, pwfx, NULL);
234 WLog_ERR(TAG,
"Failed to initialize the audio client");
238 hr = pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount);
241 WLog_ERR(TAG,
"Failed to get buffer size");
245 hr = pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioCaptureClient,
246 (
void**)&pCaptureClient);
249 WLog_ERR(TAG,
"Failed to get the capture client");
253 hnsActualDuration = (UINT32)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
255 hr = pAudioClient->lpVtbl->Start(pAudioClient);
258 WLog_ERR(TAG,
"Failed to start capture");
264 while (wfi->snd_stop == FALSE)
268 Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
270 hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
273 WLog_ERR(TAG,
"Failed to get packet length");
277 while (packetLength != 0)
279 hr = pCaptureClient->lpVtbl->GetBuffer(pCaptureClient, &pData, &numFramesAvailable,
283 WLog_ERR(TAG,
"Failed to get buffer");
289 if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT))
290 context->rdpsnd->SendSamples(context->rdpsnd, pData, packetLength,
291 (UINT16)(GetTickCount() & 0xffff));
293 hr = pCaptureClient->lpVtbl->ReleaseBuffer(pCaptureClient, numFramesAvailable);
296 WLog_ERR(TAG,
"Failed to release buffer");
300 hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
303 WLog_ERR(TAG,
"Failed to get packet length");
309 pAudioClient->lpVtbl->Stop(pAudioClient);
312 WLog_ERR(TAG,
"Failed to stop audio client");
318 if (pEnumerator != NULL)
319 pEnumerator->lpVtbl->Release(pEnumerator);
322 pDevice->lpVtbl->Release(pDevice);
324 if (pAudioClient != NULL)
325 pAudioClient->lpVtbl->Release(pAudioClient);
327 if (pCaptureClient != NULL)
328 pCaptureClient->lpVtbl->Release(pCaptureClient);