VC++ WASAPI音频捕获(麦克风或扬声器)

声音捕获PCM数据回调函数(CAudioCapture.h):

#ifndef _WAVEFORMATEX_
#define _WAVEFORMATEX_
typedef unsigned short      WORD;
typedef unsigned long       DWORD;
typedef struct tWAVEFORMATEX
{
	WORD    wFormatTag;        /* format type */
	WORD    nChannels;         /* number of channels (i.e. mono, stereo...) */
	DWORD   nSamplesPerSec;    /* sample rate */
	DWORD   nAvgBytesPerSec;   /* for buffer estimation */
	WORD    nBlockAlign;       /* block size of data */
	WORD    wBitsPerSample;    /* Number of bits per sample of mono data */
	WORD    cbSize;            /* The count in bytes of the size of
							   extra information (after cbSize) */

} WAVEFORMATEX;
typedef WAVEFORMATEX       *PWAVEFORMATEX;
#endif /* _WAVEFORMATEX_ */

#ifndef _AUDIODATA_SOURCE_
#define _AUDIODATA_SOURCE_
enum IID_Audio_Source
{
	IID_AD_SPEEKER,   // 扬声器
	IID_AD_MIC        // 麦克风
}IID_Audio_Source;
#endif

// 音频流回调
#ifndef _AUDIODATA_CALLBACK_
#define _AUDIODATA_CALLBACK_
typedef void (*LPAudioDataRealCallback)(unsigned char *pPcmData, int size, WAVEFORMATEX* wfex, enum IID_Audio_Source iidAudioSource, void* pContext);
#endif

调用接口定义(CAudioCapture.h):

// 设置音频回调函数,并开始捕获音频数据
AVFILTER_API void* AudioSourceStartCapture(LPAudioDataRealCallback pCallback, void* pContext);
// 停止音频数据捕获
AVFILTER_API bool AudioSourceStopCapture(void* pSource);

实现源码(CAudioCapture.c):

#include 
#include "CAudioCapture.h"
#include 
#include 
#include 
#include 
#include 

typedef struct tAudioSource
{
	IMMDevice*				m_pDevice;
	IAudioClient*			m_pClient;
	IAudioCaptureClient*	m_pCapture;
	IAudioRenderClient*		m_pRender;
	WAVEFORMATEX*			m_pFormat;

	enum IID_Audio_Source   m_iidAudioSource;//音频源采集方式-可以是MIC,也可以是声卡
	bool					m_bReconnecting;//设备连接标识
	HANDLE					m_hReconnectThread;//自动重连线程句柄
	bool					m_bActive;//设备是否激活
	HANDLE					m_hCaptureThread;//捕获线程句柄
	HANDLE					m_hStopSignal;//线程停止信号
	HANDLE					m_hReceiveSignal;//接受数据信号

	CRITICAL_SECTION		m_csMemLock;//数据访问互斥锁
	void*					m_memRawBuffer;//音频数据存放缓冲区
	int						m_nMemSize;//pcm数据缓冲区大小

	LPAudioDataRealCallback m_pAudioDataRealCalBack;//实时捕获数据回调函数
	void* m_pAudioDataRealCalBackUser;//实时捕获数据用户接受对象
}AudioSource, *LPAudioSource;

//申请一个音频源对象
void AllocAudioResource(LPAudioSource* ppSource);
//释放一个音频源对象
void ReleaseAudioResource(LPAudioSource pSource);
//开始捕获音频数据
void StartAudioCapture(LPAudioSource pSource, enum IID_Audio_Source iidAudioSource);
//停止捕获音频数据
void StopAudioCapture(LPAudioSource pSource);

//初始化
bool TryInitialize(LPAudioSource pSource);
bool Initialize(LPAudioSource pSource);

//初始化音频源设备
bool InitDevice(LPAudioSource pSource, IMMDeviceEnumerator *enumerator);
//初始化音频源设备
bool InitClient(LPAudioSource pSource);
bool InitRender(LPAudioSource pSource);
bool InitCapture(LPAudioSource pSource);

//自动重连线程
void Reconnect(LPAudioSource pSource);
DWORD WINAPI OnReconnectThread(LPVOID param);

//音频数据获取线程
DWORD WINAPI OnAudioCaptureThread(LPVOID param);
bool DoAudioCapture(LPAudioSource pSource);
//获取音频数据
bool ProcessAudioCaptureData(LPAudioSource pSource);
//相应数据回调
void OnCaptureDataCallback(LPAudioSource pSource, LPBYTE pData, INT nDataLen);

//所需接口GUID值定义
const IID IID_IAudioRenderClient			= { 0xF294ACFC, 0x3146, 0x4483, { 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2 } };
const IID IID_IAudioClient					= { 0x1CB9AD4C, 0xDBFA, 0x4c32, { 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2 } };
const CLSID CLSID_MMDeviceEnumerator		= { 0xBCDE0395, 0xE52F, 0x467C, { 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E } };
const IID IID_IMMDeviceEnumerator			= { 0xA95664D2, 0x9614, 0x4F35, { 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6 } };
const IID IID_IAudioCaptureClient			= { 0xC8ADBD64, 0xE71E, 0x48a0, { 0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17 } };
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT	= { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
const GUID KSDATAFORMAT_SUBTYPE_PCM			= { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };

#define BUFFER_TIME_100NS (5*10000000)
int g_ref = 0;
//申请一个音频源对象
void AllocAudioResource(LPAudioSource* ppSource)
{
	//初始化COM接口
	if ( g_ref++ == 0 )
		CoInitialize(NULL);
	//参数初始化
	*ppSource = (LPAudioSource)malloc(sizeof(AudioSource));
	((LPAudioSource)*ppSource)->m_pDevice = NULL;
	((LPAudioSource)*ppSource)->m_pClient = NULL;
	((LPAudioSource)*ppSource)->m_pCapture = NULL;
	((LPAudioSource)*ppSource)->m_pRender = NULL;
	((LPAudioSource)*ppSource)->m_pFormat = NULL;

	((LPAudioSource)*ppSource)->m_iidAudioSource = IID_AD_SPEEKER;
	((LPAudioSource)*ppSource)->m_bReconnecting = false;
	((LPAudioSource)*ppSource)->m_hReconnectThread = NULL;

	((LPAudioSource)*ppSource)->m_bActive = false;
	((LPAudioSource)*ppSource)->m_hCaptureThread = NULL;

	((LPAudioSource)*ppSource)->m_hStopSignal = CreateEvent(NULL, true, false, NULL);
	((LPAudioSource)*ppSource)->m_hReceiveSignal = CreateEvent(NULL, false, false, NULL);

	((LPAudioSource)*ppSource)->m_memRawBuffer = (char*)malloc(4096 * 1000);
	memset(((LPAudioSource)*ppSource)->m_memRawBuffer, 0, 4096 * 1000);
	((LPAudioSource)*ppSource)->m_nMemSize = 0;
	((LPAudioSource)*ppSource)->m_pAudioDataRealCalBack = NULL;
	((LPAudioSource)*ppSource)->m_pAudioDataRealCalBackUser = NULL;

	InitializeCriticalSection(&((LPAudioSource)*ppSource)->m_csMemLock);
}

//释放音频源对象
void ReleaseAudioResource(LPAudioSource pSource)
{
	StopAudioCapture(pSource);
	free(((LPAudioSource)pSource)->m_memRawBuffer);
	((LPAudioSource)pSource)->m_memRawBuffer = NULL;
	DeleteCriticalSection(&((LPAudioSource)pSource)->m_csMemLock);
	if (--g_ref == 0)
		CoUninitialize();
	free(pSource);
	pSource = NULL;
}

//开始捕获音频数据
void StartAudioCapture(LPAudioSource pSource, enum IID_Audio_Source iidAudioSource)
{
	pSource->m_iidAudioSource = iidAudioSource;

	if (!TryInitialize(pSource))
		Reconnect(pSource);
}

//停止捕获音频数据
void StopAudioCapture(LPAudioSource pSource)
{
	//发送线程停止工作信号
	SetEvent(pSource->m_hStopSignal);

	//等待捕获线程安全退出
	if (pSource->m_bActive)
	{
		WaitForSingleObject(pSource->m_hCaptureThread, INFINITE);
	}

	//等待重连线程安全退出
	if (pSource->m_hReconnectThread)
		WaitForSingleObject(pSource->m_hReconnectThread, INFINITE);

	if (pSource->m_pClient)
	{
		pSource->m_pClient->lpVtbl->Stop(pSource->m_pClient);
		pSource->m_pClient->lpVtbl->Release(pSource->m_pClient);
	}
	pSource->m_pClient = NULL;

	if (pSource->m_pCapture)
		pSource->m_pCapture->lpVtbl->Release(pSource->m_pCapture);
	pSource->m_pCapture = NULL;

	if (pSource->m_pRender)
		pSource->m_pRender->lpVtbl->Release(pSource->m_pRender);
	pSource->m_pRender = NULL;

	if (pSource->m_pDevice)
		pSource->m_pDevice->lpVtbl->Release(pSource->m_pDevice);
	pSource->m_pDevice = NULL;

	if (pSource->m_pFormat != NULL)
		CoTaskMemFree(pSource->m_pFormat);
	pSource->m_pFormat = NULL;

	pSource->m_nMemSize = 0;
}

//初始化设备
bool InitDevice(LPAudioSource pSource, IMMDeviceEnumerator *enumerator)
{
	//获取默认音频设备
	//渲染设备的数据流方向是eRender。捕获设备的数据流方向是eCapture
	HRESULT hRet = enumerator->lpVtbl->GetDefaultAudioEndpoint(
		enumerator,
		pSource->m_iidAudioSource == IID_AD_MIC ? eCapture : eRender,
		pSource->m_iidAudioSource == IID_AD_MIC ? eCommunications : eConsole,
		&pSource->m_pDevice);

	return SUCCEEDED(hRet);
}

//重设音频流输出格式
bool ResetFormatTo16Bits(WAVEFORMATEX *pwfx)
{
	bool bRet = false;

	if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT){
		pwfx->wFormatTag = WAVE_FORMAT_PCM;
		pwfx->wBitsPerSample = 16;
		pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
		pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;

		bRet = true;
	}
	else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
		PWAVEFORMATEXTENSIBLE pEx = (PWAVEFORMATEXTENSIBLE)(pwfx);
		GUID SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
		if (IsEqualGUID(&SubFormat, &pEx->SubFormat)){
			pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
			pEx->Samples.wValidBitsPerSample = 16;
			pwfx->wBitsPerSample = 16;
			pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
			pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;

			bRet = true;
		}
	}

	return bRet;
}

//初始化音频源设备
bool InitClient(LPAudioSource pSource)
{
	HRESULT		hRet;
	DWORD       dwFlags =  AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
	//激活该音频采集接口
	hRet = pSource->m_pDevice->lpVtbl->Activate(
		pSource->m_pDevice,
		&IID_IAudioClient,
		CLSCTX_ALL,
		NULL,
		(void**)&pSource->m_pClient);
	if (FAILED(hRet))
		return false;

	REFERENCE_TIME hnsDefaultDevicePeriod;
	hRet = pSource->m_pClient->lpVtbl->GetDevicePeriod(pSource->m_pClient, &hnsDefaultDevicePeriod, NULL);
	if (FAILED(hRet)) {
		return false;
	}

	//获取默认音频流格式
	hRet = pSource->m_pClient->lpVtbl->GetMixFormat(pSource->m_pClient, &pSource->m_pFormat);
	if (FAILED(hRet))
		return false;

	ResetFormatTo16Bits(pSource->m_pFormat);

	if (pSource->m_iidAudioSource == IID_AD_SPEEKER )
		dwFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;

	//初始化流
	hRet = pSource->m_pClient->lpVtbl->Initialize(
		pSource->m_pClient,
		AUDCLNT_SHAREMODE_SHARED,
		dwFlags,
		BUFFER_TIME_100NS,
		0,
		pSource->m_pFormat,
		NULL);
	if (FAILED(hRet))
		return false;

	return true;
}

//初始化音频源设备
bool InitRender(LPAudioSource pSource)
{
	WAVEFORMATEX*	pFormat;
	HRESULT         hRet;
	LPBYTE			pBuffer;
	UINT32          nBufferSize;
	IAudioClient*	pClient;
	//激活该音频采集接口
	hRet = pSource->m_pDevice->lpVtbl->Activate(
		pSource->m_pDevice,
		&IID_IAudioClient,
		CLSCTX_ALL,
		NULL,
		(void**)&pClient);
	if (FAILED(hRet))
		return false;
	
	//获取默认音频流格式
	hRet = pClient->lpVtbl->GetMixFormat(pClient, &pFormat);
	if (FAILED(hRet))
		return false;

	//初始化流
	hRet = pClient->lpVtbl->Initialize(
		pClient,
		AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK,
		BUFFER_TIME_100NS, 0, pFormat, NULL);
	if (FAILED(hRet))
		return false;

	hRet = pClient->lpVtbl->GetBufferSize(pClient, &nBufferSize);
	if (FAILED(hRet))
		return false;

	hRet = pClient->lpVtbl->GetService(pClient, &IID_IAudioRenderClient, (void**)&pSource->m_pRender);
	if (FAILED(hRet))
		return false;

	hRet = pSource->m_pRender->lpVtbl->GetBuffer(pSource->m_pRender, nBufferSize, &pBuffer);
	if (FAILED(hRet))
		return false;

	memset(pBuffer, 0, nBufferSize*pFormat->nBlockAlign);
	pSource->m_pRender->lpVtbl->ReleaseBuffer(pSource->m_pRender, nBufferSize, 0);
	pClient->lpVtbl->Release(pClient);
	pClient = NULL;
	return true;
}


bool InitCapture(LPAudioSource pSource)
{
	HRESULT hRet = pSource->m_pClient->lpVtbl->GetService(pSource->m_pClient, &IID_IAudioCaptureClient,
		(void**)&pSource->m_pCapture);
	if (FAILED(hRet))
		return false;

	hRet = pSource->m_pClient->lpVtbl->SetEventHandle(pSource->m_pClient, pSource->m_hReceiveSignal);
	if (FAILED(hRet))
		return false;

	unsigned int dwThreadId;
	pSource->m_hCaptureThread = (HANDLE)_beginthreadex(NULL,
		0,
		&OnAudioCaptureThread,
		pSource,
		THREAD_PRIORITY_NORMAL,
		&dwThreadId);
	if (!pSource->m_hCaptureThread)
		return false;

	pSource->m_pClient->lpVtbl->Start(pSource->m_pClient);
	pSource->m_bActive = TRUE;

	return true;
}

//初始化
bool Initialize(LPAudioSource pSource)
{
	IMMDeviceEnumerator* enumerator;
	HRESULT hRet;

try_again:
	hRet = CoCreateInstance(&CLSID_MMDeviceEnumerator,
		NULL, CLSCTX_ALL,
		&IID_IMMDeviceEnumerator,
		(void**)&enumerator);
	if (FAILED(hRet))
	{
		if (hRet == CO_E_FIRST)
		{
			CoInitialize(NULL);
			goto try_again;
		}
		return false;
	}

	if (!InitDevice(pSource, enumerator))
		return false;

	InitClient(pSource);
	if ( pSource->m_iidAudioSource == IID_AD_SPEEKER )
		InitRender(pSource);

	return InitCapture(pSource);
}


bool TryInitialize(LPAudioSource pSource)
{
	bool  ret = false;
	__try{
		ret = Initialize(pSource);
	}__except (0){
	}

	return ret;
}

//设备重连
void Reconnect(LPAudioSource pSource)
{
	pSource->m_bReconnecting = false;
	unsigned int dwThreadId;
	pSource->m_hReconnectThread = (HANDLE)_beginthreadex(NULL,
		0,
		&OnReconnectThread,
		pSource,
		THREAD_PRIORITY_NORMAL,
		&dwThreadId);
}


bool WaitForSignal(HANDLE hHandle, DWORD dwTimeOut)
{
	return WaitForSingleObject(hHandle, dwTimeOut) != WAIT_TIMEOUT;
}

//设备重连线程
#define RECONNECT_INTERVAL 3000
DWORD WINAPI OnReconnectThread(LPVOID param)
{
	CoInitialize(NULL);
	LPAudioSource pSource = (LPAudioSource)param;
	pSource->m_bReconnecting = true;
	while (!WaitForSignal(pSource->m_hStopSignal, RECONNECT_INTERVAL))
	{
		if (TryInitialize(pSource))
			break;
	}

	pSource->m_hReconnectThread = NULL;
	pSource->m_bReconnecting = false;
	_endthreadex(0);
	return 0;
}

//音频数据处理
bool ProcessAudioCaptureData(LPAudioSource pSource)
{
	HRESULT hRet;
	LPBYTE  pBuffer;
	UINT32  nNumFramesToRead;
	DWORD   dwFlags;
	UINT64  pos, ts;
	UINT    nNextPacketSize = 0;
	bool bFirstPacket = true;

	while (true)
	{
		hRet = pSource->m_pCapture->lpVtbl->GetNextPacketSize(pSource->m_pCapture, &nNextPacketSize);

		if (FAILED(hRet))
		{
			if (hRet != AUDCLNT_E_DEVICE_INVALIDATED)
				printf("capture->GetNextPacketSize failed: %lX\n", hRet);

			return false;
		}

		if (!nNextPacketSize)
			break;

		//锁定缓冲区,获取数据
		hRet = pSource->m_pCapture->lpVtbl->GetBuffer(pSource->m_pCapture, &pBuffer, &nNumFramesToRead, &dwFlags, &pos, &ts);
		if (FAILED(hRet))
		{
			if (hRet != AUDCLNT_E_DEVICE_INVALIDATED)
				printf("capture->GetBuffer failed: %lX\n", hRet);

			return false;
		}

		if (bFirstPacket && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) {
			printf("%s", "first audio frame");
		}
		else if (0 != dwFlags) {
			printf("IAudioCaptureClient::GetBuffer set flags to 0x%08x\n", dwFlags);
			continue;
		}

		if (0 != nNumFramesToRead)
		{
			if (dwFlags & AUDCLNT_BUFFERFLAGS_SILENT)
			{
				//////////////////////////////////////////////////////////////////////////
			}
			else
			{
				OnCaptureDataCallback(pSource, pBuffer, nNumFramesToRead);
			}
		}

		pSource->m_pCapture->lpVtbl->ReleaseBuffer(pSource->m_pCapture, nNumFramesToRead);
	}

	return true;
}


void OnCaptureDataCallback(LPAudioSource pSource, LPBYTE pData, INT nDataLen)
{
 	if (pSource->m_pAudioDataRealCalBack)
 		pSource->m_pAudioDataRealCalBack((LPBYTE)pData, nDataLen, pSource->m_pFormat, pSource->m_iidAudioSource, pSource->m_pAudioDataRealCalBackUser);
}


bool WaitForCaptureSignal(DWORD dwNumSignals, const HANDLE *hSignals,
	DWORD dwDuration)
{
	DWORD dwRet = WaitForMultipleObjects(dwNumSignals, hSignals, FALSE, dwDuration);
	return dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT;
}

//音频数据循环捕获线程
DWORD WINAPI OnAudioCaptureThread(LPVOID param)
{
	LPAudioSource pSource = (LPAudioSource)param;
	DoAudioCapture(pSource);
	_endthreadex(0);
	return 0;
}

//音频数据循环捕获线程-处理线程
bool DoAudioCapture(LPAudioSource pSource)
{
	//初始化COM接口
	CoInitialize(NULL);
	bool         bReconnect = false;
	DWORD        dwDuration = pSource->m_iidAudioSource == IID_AD_MIC ? INFINITE : 1;
	HANDLE hSignals[2] =
	{
		pSource->m_hReceiveSignal,
		pSource->m_hStopSignal
	};

	// 等待超时进入下一次数据获取
	while (WaitForCaptureSignal(2, hSignals, dwDuration))
	{
		if (!ProcessAudioCaptureData(pSource)) {
			bReconnect = true;
			break;
		}
	}

	//关闭信号
	CloseHandle(pSource->m_hReceiveSignal);
	pSource->m_hReceiveSignal = NULL;
	CloseHandle(pSource->m_hStopSignal);
	pSource->m_hStopSignal = NULL;
	//关闭线程句柄
	CloseHandle(pSource->m_hCaptureThread);
	pSource->m_hCaptureThread = NULL;
	pSource->m_bActive = false;
	return bReconnect;
}

//设置音频数据回调函数
void SetAudioCaptureDataRealCallback(LPAudioSource pSource, LPAudioDataRealCallback pAudioDataRealCalBack, void* pUser)
{
	pSource->m_pAudioDataRealCalBack = pAudioDataRealCalBack;
	pSource->m_pAudioDataRealCalBackUser = pUser;
}

// 设置音频回调函数,并开始捕获音频数据
AVFILTER_API void* AudioSourceStartCapture(LPAudioDataRealCallback pCallback, void* pUser)
{
	LPAudioSource pSource = NULL;
	AllocAudioResource(&pSource);
	SetAudioCaptureDataRealCallback(pSource, pCallback, pUser);
	if (pSource)
	{
		StartAudioCapture(pSource, IID_AD_SPEEKER); // 此处决定是采集麦克风或是扬声器
		return pSource;
	}
	else
	{
		ReleaseAudioResource(pSource);
		pSource = NULL;
		return pSource;
	}
}

// 停止音频数据捕获
AVFILTER_API bool AudioSourceStopCapture(void* pSource)
{
	SetAudioCaptureDataRealCallback(pSource, NULL, NULL);
	ReleaseAudioResource(pSource);
	return true;
}

 

你可能感兴趣的:(VC++(日积月累篇))