声音捕获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;
}