Win7 以后使用 Core Audio APIs 采集音频 IMMDevice IAudioClient IAudioCaptureClient

 

Win7 以后使用 Core Audio APIs 采集音频 IMMDevice IAudioClient IAudioCaptureClient

 

CAudioCapture::CAudioCapture()
{
	m_bRunning = false;
	m_hnsDefaultDevicePeriod = 0;
	m_pWfx = NULL;
	m_DataFlow = eCapture;
	m_hEventStarted = nullptr;
	m_hEventStop = nullptr;
	m_pDevice = nullptr;
	m_hThreadCapture = nullptr;
	m_hTimerWakeUp = nullptr;
	m_hTask = nullptr;
	memset(&m_par, 0, sizeof(m_par));

	m_pAudioClient = NULL;
	m_pAudioCaptureClient = NULL;

	m_hEventStarted = CreateEvent(nullptr, true, false, nullptr);
	m_hEventStop = CreateEvent(nullptr, true, false, nullptr);
	m_hTimerWakeUp = CreateWaitableTimer(nullptr, false, nullptr);
}
CAudioCapture::~CAudioCapture()
{
	
	SafRelease(m_pAudioClient);
	SafRelease(m_pAudioCaptureClient);
	SafRelease(m_pDevice);

	SafCloseHandle(m_hEventStarted);
	SafCloseHandle(m_hEventStop);

	if (NOT_NULLPTR(m_hTask))
	{
		AvRevertMmThreadCharacteristics(m_hTask);
		m_hTask = nullptr;
	}

	if (m_pWfx != NULL)
	{
		CoTaskMemFree(m_pWfx);
		m_pWfx = NULL;
	}
	if (NOT_NULLPTR(m_hTimerWakeUp))
	{
		CloseHandle(m_hTimerWakeUp);
		m_hTimerWakeUp = nullptr;
	}
	SafCloseHandle(m_hThreadCapture);
}

bool CAudioCapture::Init()
{
	HRESULT hr = S_OK;
	if (m_pWfx != NULL)
	{
		CoTaskMemFree(m_pWfx);
		m_pWfx = NULL;
	}
	hr = m_pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&m_pAudioClient);
	if (FAILED(hr))
	{
		return false;
	}


	hr = m_pAudioClient->GetMixFormat(&m_pWfx);
	if (FAILED(hr) || m_pWfx == NULL)
	{
		return false;
	}
	if (m_par.audioConfig.channels != 0 || m_par.audioConfig.samplesRate != 0 || m_par.audioConfig.bitsPerSample != 0 )
	{
		switch (m_pWfx->wFormatTag)
		{
			case WAVE_FORMAT_IEEE_FLOAT:
			{
				BOOL bChanged = FALSE;
				WAVEFORMATEX *pwfx = m_pWfx;
				
				if (m_par.audioConfig.bitsPerSample != 0)
				{
					pwfx->wBitsPerSample = m_par.audioConfig.bitsPerSample;
					pwfx->wFormatTag = WAVE_FORMAT_PCM;
					bChanged = TRUE;
				}
				if (m_par.audioConfig.samplesRate != 0)
				{
					pwfx->nSamplesPerSec = m_par.audioConfig.samplesRate;
					pwfx->wFormatTag = WAVE_FORMAT_PCM;
					bChanged = TRUE;
				}
				if (m_par.audioConfig.channels != 0)
				{
					pwfx->nChannels = m_par.audioConfig.channels;
					pwfx->wFormatTag = WAVE_FORMAT_PCM;
					bChanged = TRUE;
				}
				if (bChanged)
				{
					pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
					pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
				}

				break;
			}
			case WAVE_FORMAT_EXTENSIBLE:
			{
				// naked scope for case-local variable
				WAVEFORMATEX *pwfx = m_pWfx;
				PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(m_pWfx);
				if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) 
				{
					BOOL bChanged = FALSE;
					if (m_par.audioConfig.bitsPerSample != 0)
					{
						pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
					//	pwfx->wFormatTag = WAVE_FORMAT_PCM;
						pEx->Samples.wValidBitsPerSample = m_par.audioConfig.bitsPerSample;
						pwfx->wBitsPerSample = m_par.audioConfig.bitsPerSample;
						bChanged = TRUE;
					}
					if (m_par.audioConfig.samplesRate != 0)
					{
						pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
					//	pwfx->wFormatTag = WAVE_FORMAT_PCM;
						pwfx->nSamplesPerSec = m_par.audioConfig.samplesRate;
						bChanged = TRUE;
					}
					if (m_par.audioConfig.channels != 0)
					{
						pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
					//	pwfx->wFormatTag = WAVE_FORMAT_PCM;
						pwfx->nChannels = m_par.audioConfig.channels;
						bChanged = TRUE;
					}
	
					if (bChanged)
					{
						pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
						pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
					}

				}
				else 
				{
					return false;
				}
				break;
			}

		}
	}

	DWORD StreamFlags = (m_DataFlow == eRender ? AUDCLNT_STREAMFLAGS_LOOPBACK : 0);
	REFERENCE_TIME hnsRequestedDuration =  0;

	if (m_par.audioConfig.fps != 0)
	{
		hnsRequestedDuration = REFTIMES_PER_MILLISEC * (1000/ m_par.audioConfig.fps);
	}
	else
	{
		hnsRequestedDuration = REFTIMES_PER_MILLISEC * 10;
	}

	
	hr = m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, StreamFlags, hnsRequestedDuration, 0, m_pWfx, 0);
	if (FAILED(hr))
	{
		return false;
	}
	hr = m_pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&m_pAudioCaptureClient);
	if (FAILED(hr) || m_pAudioCaptureClient == NULL)
	{
		return false;
	}

	hr = m_pAudioClient->GetDevicePeriod(&m_hnsDefaultDevicePeriod, nullptr);
	if (FAILED(hr))
	{
		return false;
	}
	return true;
}
bool CAudioCapture::Start()
{
	if (m_bRunning)
	{
		return true;
	}
	SafCloseHandle(m_hThreadCapture);
	ResetEvent(m_hEventStop);
	m_hThreadCapture = (HANDLE)_beginthreadex(nullptr, 0, &CAudioCapture::_CaptureThreadProc, this, 0, nullptr);
	if (IS_NULLPTR(m_hThreadCapture))
	{
		return false;
	}
		
	
	HANDLE ahWaits[2] = { m_hEventStarted, m_hThreadCapture };
	DWORD dwWaitResult = WaitForMultipleObjects(sizeof(ahWaits) / sizeof(ahWaits[0]), ahWaits, false, INFINITE);
	if (WAIT_OBJECT_0 != dwWaitResult)
	{
		SafCloseHandle(m_hThreadCapture);
		return false;
	}

	return true;
	

}
void CAudioCapture::Stop()
{
	SetEvent(m_hEventStop);
	if (m_hThreadCapture != NULL)
	{
		WaitForSingleObject(m_hThreadCapture, 500);
		SafCloseHandle(m_hThreadCapture);
	}
	
}
static wchar_t * GetDataFlowName(EDataFlow dataFlow)
{
	if (dataFlow == eRender)
	{
		return L"Capture";
	}
	else
	{
		return L"Audio";
	}
}
void CAudioCapture::CaptureThread()
{
	
	HRESULT hr = S_OK;

	LARGE_INTEGER liFirstFire;
	liFirstFire.QuadPart = -m_hnsDefaultDevicePeriod / 2;
	LONG lTimeBetweenFires = (LONG)m_hnsDefaultDevicePeriod / REFTIMES_PER_MILLISEC / 2;
#pragma warning(disable:4800)
	bool bOk = (bool)SetWaitableTimer(m_hTimerWakeUp, &liFirstFire, lTimeBetweenFires, nullptr, nullptr, false);
	if (IS_FALSE(bOk))
	{
		return;
	}

	hr = m_pAudioClient->Start();
	if (FAILED(hr))
	{
		CancelWaitableTimer(m_hTimerWakeUp);
		return;
	}

	SetEvent(m_hEventStarted);

	HANDLE ahWait[2] = { m_hEventStop, m_hTimerWakeUp };
	DWORD  dwWaitResult;
	UINT32 uiNextPacketSize(0);
	BYTE *pData = nullptr;
	UINT32 uiNumFramesToRead;
	DWORD dwFlags;
	while (true)
	{
		dwWaitResult = WaitForMultipleObjects(sizeof(ahWait) / sizeof(ahWait[0]), ahWait, false, INFINITE);
		if (WAIT_OBJECT_0 + 1 != dwWaitResult)
		{
			break;
		}
			
		hr = m_pAudioCaptureClient->GetNextPacketSize(&uiNextPacketSize);
		if (FAILED(hr))
		{
			break;
		}

		while (uiNextPacketSize != 0)
		{
			hr = m_pAudioCaptureClient->GetBuffer(
				&pData,
				&uiNumFramesToRead,
				&dwFlags,
				nullptr,
				nullptr);
			if (FAILED(hr))
			{
				break;
			}

			if (0 != uiNumFramesToRead)
			{
				if (m_par.callback != NULL)
				{
					m_par.callback(pData, uiNumFramesToRead* m_pWfx->nBlockAlign, m_par.usrData);
				}
				//pEventHandle->OnCaptureData(pData, uiNumFramesToRead * pWfx->nBlockAlign);
			}

			m_pAudioCaptureClient->ReleaseBuffer(uiNumFramesToRead);
			hr = m_pAudioCaptureClient->GetNextPacketSize(&uiNextPacketSize);
			if (FAILED(hr))
			{
				break;
			}
		}
			

			




	}




	m_pAudioClient->Stop();
	CancelWaitableTimer(m_hTimerWakeUp);
}
UINT __stdcall CAudioCapture::_CaptureThreadProc(LPVOID param)
{
	CAudioCapture *_this = (CAudioCapture *)param;
	_this->m_bRunning = true;

	DWORD nTaskIndex = 0;
	_this->m_hTask = AvSetMmThreadCharacteristics(GetDataFlowName(_this->m_DataFlow), &nTaskIndex);
	if (IS_NULLPTR(_this->m_hTask))
	{
		return 0;
	}
	_this->CaptureThread();

	if (NOT_NULLPTR(_this->m_hTask))
	{
		AvRevertMmThreadCharacteristics(_this->m_hTask);
		_this->m_hTask = nullptr;
	}

	_this->m_bRunning = false;
	return 0;
}
static IMMDevice *findAudioDev(const char *id)
{
	
	
	IMMDeviceEnumerator *pMMDeviceEnumerator = nullptr;
	HRESULT hr = CoCreateInstance(
		__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
		__uuidof(IMMDeviceEnumerator),
		(void**)&pMMDeviceEnumerator);
	if (FAILED(hr))
	{
		return NULL;
	}


#if 1
	IMMDevice *pDevice = nullptr;
	wchar_t devId[400];
	UTF8ToUnicode(id, devId, sizeof(devId)/sizeof(wchar_t));
	pMMDeviceEnumerator->GetDevice(devId, &pDevice);
	SafRelease(pMMDeviceEnumerator);

	return pDevice;
#else

	UINT count = 0;
	IMMDeviceCollection *collect = nullptr;
	hr = pMMDeviceEnumerator->EnumAudioEndpoints(eAll, DEF_DEVICE_STATE, &collect);
	SafRelease(pMMDeviceEnumerator);

	if (collect != NULL)
	{
		collect->GetCount(&count);
		for (UINT i = 0; i < count; i++)
		{
			IMMDevice *pDevice = nullptr;
			hr = collect->Item(i, &pDevice);
			if (hr == S_OK && pDevice != NULL)
			{

				LPWSTR strid = NULL;
				hr = pDevice->GetId(&strid);
				if (hr == S_OK && strid != NULL)
				{
					char devPath[400];
					UnicodeToUTF8(strid, devPath, sizeof(devPath));
					CoTaskMemFree(strid);
					strid = NULL;
					if (strcmpix(devPath, id) == 0)
					{
						SafRelease(collect);
						return pDevice;
					}

				}

			}
			SafRelease(pDevice);
		}
		
	}


	SafRelease(collect);
	return NULL;

#endif

}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值