需求

在安防行业应用中,除了在本地看到摄像机的视频和进行音频监听外,还有一个重要的功能,那就是对讲. EasyPlayerPro-win为了减轻二次开发者的工作量,将本地音频采集也进行了集成;

功能特点


  • 支持获取本地所有音频输入设备列表;
  • 可任意选择任一音频输入设备;
  • 可设置采样率和采样精度及位率;
  • 可设置编码格式(当前支持G711A,G711U,AAC);

EasyPlayerPro Windows播放器进行本地对讲喊话音频采集功能实现_二次开发

实现流程


  1. 采用DirectSound8进行本地音频采集;
  2. 将采集到的音频数据写入编码队列;
  3. 在编码线程中获取源始音频数据,进行音频编码;
  4. 编码完成后,将编码数据回调给上层应用;

//获取声音采集设备列表
int GetAudioCaptureDeviceList(int *num, SOUND_CAPTURE_DEVICE_INFO **pDeviceInfo);


int OpenAudioCaptureDevice(int captureDeviceIndex);
int GetSupportWaveFormatList(int *num, WAVEFORMATEX **ppWaveFormatEx);
int StartCapture(int waveFormatExIndex, DirectSoundCaptureCallback callback, void *userptr);
int StopCapture();
void CloseAudioCaptureDevice();

static LPTHREAD_START_ROUTINE __stdcall _lpDirectSoundCaptureThread ( LPVOID _pParam );

代码实现

//开始采集音频
int DirectSoundCapturer::StartCapture(int waveFormatExIndex, DirectSoundCaptureCallback callback, void *userptr)
{
if (NULL == pSoundCaptureThread) return -1;
if (NULL == pSoundCaptureThread->pSupportWaveFormatEx) return -1;

if (waveFormatExIndex< 0 || waveFormatExIndex>=pSoundCaptureThread->supportWaveFormatExCount) return -2;

HRESULT hr = S_OK;

WAVEFORMATEX *_wfxInput = (WAVEFORMATEX*)&pSoundCaptureThread->pSupportWaveFormatEx[waveFormatExIndex];

memcpy(&pSoundCaptureThread->inWaveFormatEx, _wfxInput, sizeof(WAVEFORMATEX));

DSCBUFFERDESC dscbd;
ZeroMemory( &dscbd, sizeof(DSCBUFFERDESC) );


pSoundCaptureThread->dwNotifySize = max( 1024, _wfxInput->nAvgBytesPerSec / 8 );
pSoundCaptureThread->dwNotifySize -= pSoundCaptureThread->dwNotifySize % _wfxInput->nBlockAlign;
pSoundCaptureThread->dwCaptureBufferSize = pSoundCaptureThread->dwNotifySize * 16;

dscbd.dwSize = sizeof(DSCBUFFERDESC);
dscbd.dwBufferBytes = pSoundCaptureThread->dwCaptureBufferSize;
dscbd.lpwfxFormat = _wfxInput;
hr = pSoundCaptureThread->lpDirectSoundCapture8->CreateCaptureBuffer( &dscbd, &pSoundCaptureThread->lpDSBCapture, NULL);

if (FAILED(hr)) return -3;
if (NULL == pSoundCaptureThread->lpDSBCapture) return -4;

pSoundCaptureThread->dwNextCaptureOffset = 0;

hr = pSoundCaptureThread->lpDSBCapture->QueryInterface( IID_IDirectSoundNotify, (PVOID*)&pSoundCaptureThread->lpDSNotify );
for( INT i = 0; i < 16; i++ )
{
pSoundCaptureThread->DSBPosNotify[i].dwOffset = (pSoundCaptureThread->dwNotifySize * i) + pSoundCaptureThread->dwNotifySize - 1;
pSoundCaptureThread->DSBPosNotify[i].hEventNotify = pSoundCaptureThread->hCaptureNotifyEvent;
}
hr = pSoundCaptureThread->lpDSNotify->SetNotificationPositions( 16, pSoundCaptureThread->DSBPosNotify);

hr = pSoundCaptureThread->lpDSBCapture->Start( DSCBSTART_LOOPING );
//m_fIsCapture = TRUE;

if (SUCCEEDED(hr))
{
if (NULL == pSoundCaptureThread->hCaptureNotifyThread)
{
pSoundCaptureThread->flag = 0x01;
pSoundCaptureThread->userPtr = this;
pSoundCaptureThread->captureCallback = callback;
pSoundCaptureThread->callbackUserPtr = userptr;
pSoundCaptureThread->hCaptureNotifyThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_lpDirectSoundCaptureThread,
pSoundCaptureThread, 0, NULL);
while (pSoundCaptureThread->flag!=0x02 && pSoundCaptureThread->flag!=0x00) {Sleep(100);}
}
}

return hr;
}


//获取源始音频数据
int DirectSoundCapturer::ProcessCaptureData()
{
HRESULT hrRet = 0;
LONG lLockSize;

if (NULL == pSoundCaptureThread) return -1;
if (pSoundCaptureThread->flag == 0x03) return -1;
if (NULL == pSoundCaptureThread->lpDSBCapture) return -1;

do {

DWORD dwCapturePos, dwReadPos;
hrRet = pSoundCaptureThread->lpDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos );

lLockSize = dwReadPos - pSoundCaptureThread->dwNextCaptureOffset;
if( lLockSize < 0 ) lLockSize += pSoundCaptureThread->dwCaptureBufferSize;

// Block align lock size so that we are always write on a boundary
lLockSize -= (lLockSize % pSoundCaptureThread->dwNotifySize);
if( lLockSize == 0 ) {
hrRet = -1;
break;
}

PVOID pCapturedData[2] = {NULL, NULL};
DWORD dwCaptureLength[2] = {0, 0};

// Lock the capture buffer down
hrRet = pSoundCaptureThread->lpDSBCapture->Lock( pSoundCaptureThread->dwNextCaptureOffset,
lLockSize,
&pCapturedData[0],
&dwCaptureLength[0],
&pCapturedData[1],
&dwCaptureLength[1], 0L );
if( FAILED( hrRet ) ) {
hrRet = -2;
break;
}

if (NULL != pSoundCaptureThread->captureCallback)
{
pSoundCaptureThread->captureCallback(&pSoundCaptureThread->inWaveFormatEx, pSoundCaptureThread->callbackUserPtr,
(unsigned char *)pCapturedData[0], (int)dwCaptureLength[0], (unsigned char *)pCapturedData[1], (int)dwCaptureLength[1]);
}

pSoundCaptureThread->dwNextCaptureOffset += dwCaptureLength[0];
pSoundCaptureThread->dwNextCaptureOffset %= pSoundCaptureThread->dwCaptureBufferSize; // Circular buffer

if( pCapturedData[1] != NULL ) {
pSoundCaptureThread->dwNextCaptureOffset += dwCaptureLength[1];
pSoundCaptureThread->dwNextCaptureOffset %= pSoundCaptureThread->dwCaptureBufferSize; // Circular buffer
}

pSoundCaptureThread->lpDSBCapture->Unlock( pCapturedData[0], dwCaptureLength[0],
pCapturedData[1], dwCaptureLength[1] );
} while(0);

return hrRet;
}

关于EasyPlayerPro播放器

EasyPlayerPro是一款全功能的流媒体播放器,支持RTSP、RTMP、HTTP、HLS、UDP、RTP、File等多种流媒体协议播放、支持本地文件播放,支持本地抓拍、本地录像、播放旋转、多屏播放、倍数播放等多种功能特性,核心基于ffmpeg,稳定、高效、可靠、可控,支持Windows、Android、iOS三个平台,目前在多家教育、安防、行业型公司,都得到的应用,广受好评!


EasyPlayerPro:​​https://github.com/EasyDSS/EasyPlayerPro​