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 
   23DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92,
 
   25DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36,
 
   27DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03,
 
   29DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c,
 
   34wfPeerContext* latestPeer = NULL;
 
   36int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
 
   42int 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);
 
   66int 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 output 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);
 
  168DWORD 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);