#include"libaudiomgr_win.h" #include #include // Macro that releases a COM object if not NULL. #define SAFE_RELEASE(p) \ do { \ if ((p)) { \ (p)->Release(); \ (p) = NULL; \ } \ } while (0) typedef struct tagAUDIO_DEVICE_INFO{ char szDeviceName[MAX_PATH]; char szDeviceID[MAX_PATH]; } AUDIO_DEVICE_INFO, *PAUDIO_DEVICE_INFO; AudioMgrImpl::AudioMgrImpl(audiomgr_callback_t* pCallback) { memcpy(&m_callback, pCallback, sizeof(audiomgr_callback_t)); m_pEnumerator = NULL; m_builtInAecEnabled = false; m_dmo = NULL; m_ptrCaptureCollection = NULL; m_ptrDeviceIn = NULL; m_usingInputDeviceIndex = false; m_inputDeviceIndex = 0; } AudioMgrImpl::~AudioMgrImpl() { } int AudioMgrImpl::audio_mgr_destroy() { delete this; return 0; } void AudioMgrImpl::audiolog(const char* fmt, ...) { if (m_callback.debug) { va_list arg; va_start(arg, fmt); if (*m_callback.debug) { (*m_callback.debug)(m_callback.user_data, fmt, arg); } va_end(arg); } } int AudioMgrImpl::audio_get_device_count(bool binput) { UINT uDevCount = 0; IMMDeviceCollection *pEndpoints = NULL; HRESULT hr(S_OK); hr = m_pEnumerator->EnumAudioEndpoints(binput ? eCapture : eRender, DEVICE_STATE_ACTIVE, (IMMDeviceCollection**)&pEndpoints); if (FAILED(hr)){ SAFE_RELEASE(pEndpoints); return 0; } hr = pEndpoints->GetCount(&uDevCount); if (FAILED(hr)){ SAFE_RELEASE(pEndpoints); return 0; } SAFE_RELEASE(pEndpoints); return static_cast(uDevCount); } int AudioMgrImpl::audio_get_device_name(char* pstrbuf, size_t ulen, bool binput, unsigned int uindex) { int iret = -1; IMMDeviceCollection *pDeviceCollection = NULL; HRESULT hr(S_OK); hr = m_pEnumerator->EnumAudioEndpoints(binput ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } UINT nCount = 0; hr = pDeviceCollection->GetCount(&nCount); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } if (uindex < nCount) { IMMDevice *pDevice = NULL; hr = pDeviceCollection->Item(uindex, &pDevice); if (SUCCEEDED(hr)) { IPropertyStore *pPS = NULL; PROPVARIANT value; PropVariantInit(&value); pDevice->OpenPropertyStore(STGM_READ, &pPS); pPS->GetValue(PKEY_Device_FriendlyName, &value); WideCharToMultiByte(CP_ACP, 0, value.pwszVal, -1, pstrbuf, ulen - 1, 0, 0); PropVariantClear(&value); pPS->Release(); } } SAFE_RELEASE(pDeviceCollection); return iret; } int AudioMgrImpl::audio_get_device_id(const char* pstrname, bool binput) { int iret = -1; if (NULL == pstrname){ return iret; } IMMDeviceCollection *pDeviceCollection = NULL; HRESULT hr(S_OK); hr = m_pEnumerator->EnumAudioEndpoints(binput ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } UINT nCount = 0; hr = pDeviceCollection->GetCount(&nCount); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } for (UINT index = 0; index < nCount; ++index) { IMMDevice *pDevice = NULL; hr = pDeviceCollection->Item(index, &pDevice); if (SUCCEEDED(hr)) { char name[MAX_PATH] = {0}; IPropertyStore *pPS = NULL; PROPVARIANT value; PropVariantInit(&value); pDevice->OpenPropertyStore(STGM_READ, &pPS); pPS->GetValue(PKEY_Device_FriendlyName, &value); WideCharToMultiByte(CP_ACP, 0, value.pwszVal, -1, name, sizeof(name)-1, 0, 0); PropVariantClear(&value); pPS->Release(); if (strstr(name, pstrname)) { iret = index; pDevice->Release(); break; } else { pDevice->Release(); } } else { break; } } SAFE_RELEASE(pDeviceCollection); return iret; } rvc_audio_device_t* AudioMgrImpl::audio_get_device_infos(bool binput, int index) { return NULL; } int AudioMgrImpl::audio_get_device_volume(int* ivolume, const char* pstrname, bool binput) { int iret = -1; IMMDeviceCollection *pDeviceCollection = NULL; IAudioEndpointVolume *pAudioEndpointVolume; HRESULT hr(S_OK); hr = m_pEnumerator->EnumAudioEndpoints(binput ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection); if (FAILED(hr)) { SAFE_RELEASE(pDeviceCollection); return -1; } UINT nCount = 0; hr = pDeviceCollection->GetCount(&nCount); if (FAILED(hr)){ return -1; } for (UINT i = 0; i < nCount; ++i) { IMMDevice *pDevice = NULL; hr = pDeviceCollection->Item(i, &pDevice); if (SUCCEEDED(hr)) { char name[MAX_PATH] = {0}; IPropertyStore *pPS = NULL; PROPVARIANT value; PropVariantInit(&value); pDevice->OpenPropertyStore(STGM_READ, &pPS); pPS->GetValue(PKEY_Device_FriendlyName, &value); WideCharToMultiByte(CP_ACP, 0, value.pwszVal, -1, name, sizeof(name)-1, 0, 0); PropVariantClear(&value); pPS->Release(); if (strstr(name, pstrname)) { hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void**)&pAudioEndpointVolume); if (SUCCEEDED(hr)) { float fValue = 0.0; hr = pAudioEndpointVolume->GetMasterVolumeLevelScalar(&fValue); if (SUCCEEDED(hr)){ *ivolume = static_cast(fValue*100); iret = 0; } } pDevice->Release(); break; } else { pDevice->Release(); } } else { break; } } SAFE_RELEASE(pDeviceCollection); return iret; } int AudioMgrImpl::audio_set_device_volume(int ivolume, const char* pstrname, bool binput) { int iret = -1; IMMDeviceCollection *pDeviceCollection = NULL; IAudioEndpointVolume *pAudioEndpointVolume; GUID guidContext; HRESULT hr(S_OK); hr = CoCreateGuid(&guidContext); if (FAILED(hr)){ return -1; } hr = m_pEnumerator->EnumAudioEndpoints(binput ? eCapture : eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } UINT nCount = 0; hr = pDeviceCollection->GetCount(&nCount); if (FAILED(hr)){ SAFE_RELEASE(pDeviceCollection); return -1; } for (UINT i = 0; i < nCount; ++i) { IMMDevice *pDevice = NULL; hr = pDeviceCollection->Item(i, &pDevice); if (SUCCEEDED(hr)) { char name[MAX_PATH] = {0}; IPropertyStore *pPS = NULL; PROPVARIANT value; PropVariantInit(&value); pDevice->OpenPropertyStore(STGM_READ, &pPS); pPS->GetValue(PKEY_Device_FriendlyName, &value); WideCharToMultiByte(CP_ACP, 0, value.pwszVal, -1, name, sizeof(name)-1, 0, 0); PropVariantClear(&value); pPS->Release(); if (strstr(name, pstrname)) { hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void**)&pAudioEndpointVolume); if (SUCCEEDED(hr)) { hr = pAudioEndpointVolume->SetMasterVolumeLevelScalar(ivolume/100.f, &guidContext); if (SUCCEEDED(hr)){ iret = 0; } } pDevice->Release(); break; } else { pDevice->Release(); } } else { break; } } SAFE_RELEASE(pDeviceCollection); return iret; } int AudioMgrImpl::audio_mgr_initialize() { int iret = -1; CoInitialize(NULL); HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&m_pEnumerator); if (!FAILED(hr)){ iret = 0; } // DMO initialization for built-in WASAPI AEC. { IMediaObject* ptrDMO = NULL; hr = CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER, IID_IMediaObject, reinterpret_cast(&ptrDMO)); if (FAILED(hr) || ptrDMO == NULL) { // Since we check that _dmo is non-NULL in EnableBuiltInAEC(), the // feature is prevented from being enabled. m_builtInAecEnabled = false; //_TraceCOMError(hr); } m_dmo = ptrDMO; SAFE_RELEASE(ptrDMO); } return iret; } int AudioMgrImpl::set_audio_capture_params(audiocap_param_t* param) { int iret = -1; int index = param->ideviceid; //UINT nDevices = RecordingDevices(); //if (index < 0 || index >(nDevices - 1)) { // RTC_LOG(LS_ERROR) << "device index is out of range [0," << (nDevices - 1) // << "]"; // return -1; //} HRESULT hr(S_OK); assert(m_ptrCaptureCollection != NULL); // Select an endpoint capture device given the specified index SAFE_RELEASE(m_ptrDeviceIn); hr = m_ptrCaptureCollection->Item(index, &m_ptrDeviceIn); if (FAILED(hr)) { //_TraceCOMError(hr); SAFE_RELEASE(m_ptrDeviceIn); return -1; } WCHAR szDeviceName[MAX_PATH] = {0}; const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0]; // Get the endpoint device's friendly-name if (getdevicename(m_ptrDeviceIn, szDeviceName, bufferLen) == 0) { audiolog("friendly name: %s.", szDeviceName); } m_usingInputDeviceIndex = true; m_inputDeviceIndex = index; return iret; } int AudioMgrImpl::audio_mgr_terminate() { int iret = -1; SAFE_RELEASE(m_pEnumerator); CoUninitialize(); iret = 0; return iret; } int AudioMgrImpl::stop_audio_capture() { return 0; } int AudioMgrImpl::start_audio_capture() { return 0; } int32_t AudioMgrImpl::getlistdevice(EDataFlow dir, int index, IMMDevice** ppDevice) { HRESULT hr(S_OK); IMMDeviceCollection *pCollection = NULL; hr = m_pEnumerator->EnumAudioEndpoints( dir, DEVICE_STATE_ACTIVE, // only active endpoints are OK &pCollection); if (FAILED(hr)){ SAFE_RELEASE(pCollection); return -1; } hr = pCollection->Item(index,ppDevice); if (FAILED(hr)){ SAFE_RELEASE(pCollection); return -1; } return 0; } int32_t AudioMgrImpl::getdevicename(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen) { audiolog("%s.", __FUNCTION__); static const WCHAR szDefault[] = L""; HRESULT hr = E_FAIL; IPropertyStore* pProps = NULL; PROPVARIANT varName; assert(pszBuffer != NULL); assert(bufferLen > 0); if (pDevice != NULL) { hr = pDevice->OpenPropertyStore(STGM_READ, &pProps); if (FAILED(hr)) { audiolog("IMMDevice::OpenPropertyStore failed, hr = 0x%08x.",hr); } } // Initialize container for property value. PropVariantInit(&varName); if (SUCCEEDED(hr)) { // Get the endpoint device's friendly-name property. hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { audiolog("IPropertyStore::GetValue failed, hr = 0x%08x", hr); } } if ((SUCCEEDED(hr)) && (VT_EMPTY == varName.vt)) { hr = E_FAIL; audiolog("IPropertyStore::GetValue returned no value, hr = 0x%08x", hr); } if ((SUCCEEDED(hr)) && (VT_LPWSTR != varName.vt)) { // The returned value is not a wide null terminated string. hr = E_UNEXPECTED; audiolog("IPropertyStore::GetValue returned unexpected type, hr = 0x%08x", hr); } if (SUCCEEDED(hr) && (varName.pwszVal != NULL)) { // Copy the valid device name to the provided ouput buffer. wcsncpy_s(pszBuffer, bufferLen, varName.pwszVal, _TRUNCATE); } else { // Failed to find the device name. wcsncpy_s(pszBuffer, bufferLen, szDefault, _TRUNCATE); } PropVariantClear(&varName); SAFE_RELEASE(pProps); return 0; }