Blame server/Windows/wf_wasapi.c

Packit 1fb8d4
Packit 1fb8d4
#include "wf_wasapi.h"
Packit 1fb8d4
#include "wf_info.h"
Packit 1fb8d4
Packit 1fb8d4
#include <initguid.h>
Packit 1fb8d4
#include <mmdeviceapi.h>
Packit 1fb8d4
#include <Functiondiscoverykeys_devpkey.h>
Packit 1fb8d4
#include <Audioclient.h>
Packit 1fb8d4
Packit 1fb8d4
#define TAG SERVER_TAG("windows")
Packit 1fb8d4
Packit 1fb8d4
//#define REFTIMES_PER_SEC  10000000
Packit 1fb8d4
//#define REFTIMES_PER_MILLISEC  10000
Packit 1fb8d4
Packit 1fb8d4
#define REFTIMES_PER_SEC  100000
Packit 1fb8d4
#define REFTIMES_PER_MILLISEC  100
Packit 1fb8d4
Packit 1fb8d4
//#define REFTIMES_PER_SEC  50000
Packit 1fb8d4
//#define REFTIMES_PER_MILLISEC  50
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C,
Packit 1fb8d4
	    0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
Packit 1fb8d4
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35,
Packit 1fb8d4
	    0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
Packit 1fb8d4
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
Packit 1fb8d4
DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
Packit 1fb8d4
Packit 1fb8d4
LPWSTR devStr = NULL;
Packit 1fb8d4
wfPeerContext* latestPeer = NULL;
Packit 1fb8d4
Packit 1fb8d4
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
Packit 1fb8d4
{
Packit 1fb8d4
	latestPeer = peer;
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
int wf_wasapi_activate(RdpsndServerContext* context)
Packit 1fb8d4
{
Packit 1fb8d4
	wchar_t * pattern = L"Stereo Mix";
Packit 1fb8d4
	HANDLE hThread;
Packit 1fb8d4
Packit 1fb8d4
	wf_wasapi_get_device_string(pattern, &devStr);
Packit 1fb8d4
Packit 1fb8d4
	if (devStr == NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to match for output device! Disabling rdpsnd.");
Packit 1fb8d4
		return 1;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	WLog_DBG(TAG, "RDPSND (WASAPI) Activated");
Packit 1fb8d4
	if (!(hThread = CreateThread(NULL, 0, wf_rdpsnd_wasapi_thread, latestPeer, 0, NULL)))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "CreateThread failed");
Packit 1fb8d4
		return 1;
Packit 1fb8d4
	}
Packit 1fb8d4
	CloseHandle(hThread);
Packit 1fb8d4
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR * deviceStr)
Packit 1fb8d4
{
Packit 1fb8d4
	HRESULT hr;
Packit 1fb8d4
	IMMDeviceEnumerator *pEnumerator = NULL;
Packit 1fb8d4
	IMMDeviceCollection *pCollection = NULL;
Packit 1fb8d4
	IMMDevice *pEndpoint = NULL;
Packit 1fb8d4
	IPropertyStore *pProps = NULL;
Packit 1fb8d4
	LPWSTR pwszID = NULL;
Packit 1fb8d4
	unsigned int count, i;
Packit 1fb8d4
Packit 1fb8d4
	CoInitialize(NULL);
Packit 1fb8d4
	hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **) &pEnumerator);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to cocreate device enumerator");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eCapture, DEVICE_STATE_ACTIVE, &pCollection);
Packit 1fb8d4
	if ( FAILED(hr) )
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to create endpoint collection");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pCollection->lpVtbl->GetCount(pCollection, &count);
Packit 1fb8d4
	WLog_INFO(TAG, "Num endpoints: %u", count);
Packit 1fb8d4
Packit 1fb8d4
	if (count == 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "No endpoints!");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	for (i = 0; i < count; ++i)
Packit 1fb8d4
	{
Packit 1fb8d4
		PROPVARIANT nameVar;
Packit 1fb8d4
		PropVariantInit(&nameVar);
Packit 1fb8d4
Packit 1fb8d4
		hr = pCollection->lpVtbl->Item(pCollection, i, &pEndpoint);
Packit 1fb8d4
		if ( FAILED(hr) )
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to get endpoint %u", i);
Packit 1fb8d4
			exit(1);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		hr = pEndpoint->lpVtbl->GetId(pEndpoint, &pwszID);
Packit 1fb8d4
		if ( FAILED(hr) )
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to get endpoint ID");
Packit 1fb8d4
			exit(1);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		hr = pEndpoint->lpVtbl->OpenPropertyStore(pEndpoint, STGM_READ, &pProps);
Packit 1fb8d4
		if ( FAILED(hr) )
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to open property store");
Packit 1fb8d4
			exit(1);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		hr = pProps->lpVtbl->GetValue(pProps, &PKEY_Device_FriendlyName, &nameVar);
Packit 1fb8d4
		if ( FAILED(hr) )
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to get device friendly name");
Packit 1fb8d4
			exit(1);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		//do this a more reliable way
Packit 1fb8d4
		if (wcscmp(pattern, nameVar.pwszVal) < 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			unsigned int devStrLen;
Packit 1fb8d4
			WLog_INFO(TAG, "Using sound ouput endpoint: [%s] (%s)", nameVar.pwszVal, pwszID);
Packit 1fb8d4
			//WLog_INFO(TAG, "matched %d characters", wcscmp(pattern, nameVar.pwszVal);
Packit 1fb8d4
			devStrLen = wcslen(pwszID);
Packit 1fb8d4
			*deviceStr = (LPWSTR) calloc(devStrLen + 1, 2);
Packit 1fb8d4
			if (!deviceStr)
Packit 1fb8d4
				return -1;
Packit 1fb8d4
			wcscpy_s(*deviceStr, devStrLen+1, pwszID);
Packit 1fb8d4
		}
Packit 1fb8d4
		CoTaskMemFree(pwszID);
Packit 1fb8d4
		pwszID = NULL;
Packit 1fb8d4
		PropVariantClear(&nameVar);
Packit 1fb8d4
Packit 1fb8d4
		pProps->lpVtbl->Release(pProps);
Packit 1fb8d4
		pProps = NULL;
Packit 1fb8d4
Packit 1fb8d4
		pEndpoint->lpVtbl->Release(pEndpoint);
Packit 1fb8d4
		pEndpoint = NULL;
Packit 1fb8d4
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pCollection->lpVtbl->Release(pCollection);
Packit 1fb8d4
	pCollection = NULL;
Packit 1fb8d4
Packit 1fb8d4
	pEnumerator->lpVtbl->Release(pEnumerator);
Packit 1fb8d4
	pEnumerator = NULL;
Packit 1fb8d4
	CoUninitialize();
Packit 1fb8d4
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
Packit 1fb8d4
{
Packit 1fb8d4
	IMMDeviceEnumerator *pEnumerator = NULL;
Packit 1fb8d4
	IMMDevice *pDevice = NULL;
Packit 1fb8d4
	IAudioClient *pAudioClient = NULL;
Packit 1fb8d4
	IAudioCaptureClient *pCaptureClient = NULL;
Packit 1fb8d4
	WAVEFORMATEX *pwfx = NULL;
Packit 1fb8d4
	HRESULT hr;
Packit 1fb8d4
	REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
Packit 1fb8d4
	REFERENCE_TIME hnsActualDuration;
Packit 1fb8d4
	UINT32 bufferFrameCount;
Packit 1fb8d4
	UINT32 numFramesAvailable;
Packit 1fb8d4
	UINT32 packetLength = 0;
Packit 1fb8d4
	UINT32 dCount = 0;
Packit 1fb8d4
	BYTE *pData;
Packit 1fb8d4
Packit 1fb8d4
	wfPeerContext* context;
Packit 1fb8d4
	wfInfo* wfi;
Packit 1fb8d4
Packit 1fb8d4
	wfi = wf_info_get_instance();
Packit 1fb8d4
	context = (wfPeerContext*)lpParam;
Packit 1fb8d4
Packit 1fb8d4
	CoInitialize(NULL);
Packit 1fb8d4
	hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **) &pEnumerator);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to cocreate device enumerator");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pEnumerator->lpVtbl->GetDevice(pEnumerator, devStr, &pDevice);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to cocreate get device");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&pAudioClient);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to activate audio client");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pAudioClient->lpVtbl->GetMixFormat(pAudioClient, &pwfx);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get mix format");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pwfx->wFormatTag = wfi->agreed_format->wFormatTag;
Packit 1fb8d4
	pwfx->nChannels = wfi->agreed_format->nChannels;
Packit 1fb8d4
	pwfx->nSamplesPerSec = wfi->agreed_format->nSamplesPerSec;
Packit 1fb8d4
	pwfx->nAvgBytesPerSec = wfi->agreed_format->nAvgBytesPerSec;
Packit 1fb8d4
	pwfx->nBlockAlign = wfi->agreed_format->nBlockAlign;
Packit 1fb8d4
	pwfx->wBitsPerSample = wfi->agreed_format->wBitsPerSample;
Packit 1fb8d4
	pwfx->cbSize = wfi->agreed_format->cbSize;
Packit 1fb8d4
Packit 1fb8d4
	hr = pAudioClient->lpVtbl->Initialize(
Packit 1fb8d4
		pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0,
Packit 1fb8d4
		hnsRequestedDuration, 0, pwfx, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to initialize the audio client");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get buffer size");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hr = pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioCaptureClient, (void **) &pCaptureClient);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to get the capture client");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	hnsActualDuration = (UINT32)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
Packit 1fb8d4
Packit 1fb8d4
	hr = pAudioClient->lpVtbl->Start(pAudioClient);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to start capture");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	dCount = 0;
Packit 1fb8d4
Packit 1fb8d4
	while (wfi->snd_stop == FALSE)
Packit 1fb8d4
	{
Packit 1fb8d4
		DWORD flags;
Packit 1fb8d4
Packit 1fb8d4
		Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
Packit 1fb8d4
Packit 1fb8d4
		hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
Packit 1fb8d4
		if (FAILED(hr))
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_ERR(TAG, "Failed to get packet length");
Packit 1fb8d4
			exit(1);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		while (packetLength != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			hr = pCaptureClient->lpVtbl->GetBuffer(pCaptureClient, &pData, &numFramesAvailable, &flags, NULL, NULL);
Packit 1fb8d4
			if (FAILED(hr))
Packit 1fb8d4
			{
Packit 1fb8d4
				WLog_ERR(TAG, "Failed to get buffer");
Packit 1fb8d4
				exit(1);
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			//Here we are writing the audio data
Packit 1fb8d4
			//not sure if this flag is ever set by the system; msdn is not clear about it
Packit 1fb8d4
			if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT))
Packit 1fb8d4
				context->rdpsnd->SendSamples(context->rdpsnd, pData, packetLength, (UINT16)(GetTickCount() & 0xffff));
Packit 1fb8d4
Packit 1fb8d4
			hr = pCaptureClient->lpVtbl->ReleaseBuffer(pCaptureClient, numFramesAvailable);
Packit 1fb8d4
			if (FAILED(hr))
Packit 1fb8d4
			{
Packit 1fb8d4
				WLog_ERR(TAG, "Failed to release buffer");
Packit 1fb8d4
				exit(1);
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
Packit 1fb8d4
			if (FAILED(hr))
Packit 1fb8d4
			{
Packit 1fb8d4
				WLog_ERR(TAG, "Failed to get packet length");
Packit 1fb8d4
				exit(1);
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	pAudioClient->lpVtbl->Stop(pAudioClient);
Packit 1fb8d4
	if (FAILED(hr))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "Failed to stop audio client");
Packit 1fb8d4
		exit(1);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	CoTaskMemFree(pwfx);
Packit 1fb8d4
Packit 1fb8d4
	if (pEnumerator != NULL)
Packit 1fb8d4
		pEnumerator->lpVtbl->Release(pEnumerator);
Packit 1fb8d4
Packit 1fb8d4
	if (pDevice != NULL)
Packit 1fb8d4
		pDevice->lpVtbl->Release(pDevice);
Packit 1fb8d4
Packit 1fb8d4
	if (pAudioClient != NULL)
Packit 1fb8d4
		pAudioClient->lpVtbl->Release(pAudioClient);
Packit 1fb8d4
Packit 1fb8d4
	if (pCaptureClient != NULL)
Packit 1fb8d4
		pCaptureClient->lpVtbl->Release(pCaptureClient);
Packit 1fb8d4
Packit 1fb8d4
	CoUninitialize();
Packit 1fb8d4
Packit 1fb8d4
	return 0;
Packit 1fb8d4
}