425 lines
9.3 KiB
C++
425 lines
9.3 KiB
C++
/***************************************************************************************
|
|
*
|
|
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
*
|
|
* By downloading, copying, installing or using the software you agree to this license.
|
|
* If you do not agree to this license, do not download, install,
|
|
* copy or use the software.
|
|
*
|
|
* Copyright (C) 2014-2022, Happytimesoft Corporation, all rights reserved.
|
|
*
|
|
* Redistribution and use in binary forms, with or without modification, are permitted.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed
|
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the License.
|
|
*
|
|
****************************************************************************************/
|
|
|
|
#include "sys_inc.h"
|
|
#include "audio_capture_win.h"
|
|
#include "lock.h"
|
|
#include <locale.h>
|
|
|
|
|
|
/***************************************************************************************/
|
|
#define HTAUDIOC_CHNUMS 2
|
|
#define HTAUDIOC_SAMPLE 1024
|
|
#define HTAUDIOC_BPS 2
|
|
#define HTAUDIOC_CHUNKS 8
|
|
|
|
#define HTAUDIOCBUF_MAX (HTAUDIOC_CHUNKS * HTAUDIOC_CHNUMS * HTAUDIOC_BPS * HTAUDIOC_SAMPLE)
|
|
|
|
/**************************************************************************************/
|
|
|
|
int CWAudioCapture::m_nDevNums = 0;
|
|
void * CWAudioCapture::m_pDevNumsMutes = sys_os_create_mutex();
|
|
DSOUND_DEVICE CWAudioCapture::m_devices[MAX_AUDIO_DEV_NUMS];
|
|
|
|
/**************************************************************************************/
|
|
|
|
BOOL CALLBACK DevEnumCallback(LPGUID guid, LPCTSTR desc, LPCTSTR module, LPVOID data)
|
|
{
|
|
if (guid != NULL) // skip default device
|
|
{
|
|
DSOUND_DEVICE * p_device = &CWAudioCapture::m_devices[CWAudioCapture::m_nDevNums];
|
|
|
|
memcpy(&p_device->guid, guid, sizeof(GUID));
|
|
_tcscpy(p_device->desc, desc);
|
|
|
|
CWAudioCapture::m_nDevNums++;
|
|
if (CWAudioCapture::m_nDevNums >= MAX_AUDIO_DEV_NUMS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************************/
|
|
static void * audioCaptureThread(void * argv)
|
|
{
|
|
CWAudioCapture *capture = (CWAudioCapture *)argv;
|
|
|
|
capture->captureThread();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***************************************************************************************/
|
|
CWAudioCapture::CWAudioCapture()
|
|
: CAudioCapture()
|
|
, m_nLastChunk(0)
|
|
, m_pDSound8(NULL)
|
|
, m_pDSoundBuffer(NULL)
|
|
{
|
|
|
|
}
|
|
|
|
CWAudioCapture::~CWAudioCapture()
|
|
{
|
|
stopCapture();
|
|
}
|
|
|
|
CAudioCapture * CWAudioCapture::getInstance(int devid)
|
|
{
|
|
if (devid < 0 || devid >= MAX_AUDIO_DEV_NUMS)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
sys_os_mutex_enter(m_pInstMutex);
|
|
|
|
if (NULL == m_pInstance[devid])
|
|
{
|
|
m_pInstance[devid] = new CWAudioCapture;
|
|
if (m_pInstance[devid])
|
|
{
|
|
m_pInstance[devid]->m_nRefCnt++;
|
|
m_pInstance[devid]->m_nDevIndex = devid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pInstance[devid]->m_nRefCnt++;
|
|
}
|
|
|
|
sys_os_mutex_leave(m_pInstMutex);
|
|
|
|
return m_pInstance[devid];
|
|
}
|
|
|
|
int CWAudioCapture::getDeviceNums()
|
|
{
|
|
CLock lock(m_pDevNumsMutes);
|
|
|
|
m_nDevNums = 0;
|
|
|
|
DirectSoundCaptureEnumerate(DevEnumCallback, NULL);
|
|
|
|
return m_nDevNums > MAX_AUDIO_DEV_NUMS ? MAX_AUDIO_DEV_NUMS : m_nDevNums;
|
|
}
|
|
|
|
void CWAudioCapture::listDevice()
|
|
{
|
|
CLock lock(m_pDevNumsMutes);
|
|
|
|
m_nDevNums = 0;
|
|
|
|
DirectSoundCaptureEnumerate(DevEnumCallback, NULL);
|
|
|
|
printf("\r\nAvailable audio capture device : \r\n\r\n");
|
|
|
|
for (int i = 0; i < m_nDevNums; i++)
|
|
{
|
|
DSOUND_DEVICE * p_device = &CWAudioCapture::m_devices[i];
|
|
|
|
char * locale = setlocale(LC_ALL, ".UTF8");
|
|
_tprintf(_T("index : %d, name : %ws\r\n"), i, p_device->desc);
|
|
setlocale(LC_ALL, locale);
|
|
}
|
|
}
|
|
|
|
int CWAudioCapture::getDeviceIndex(const char * name)
|
|
{
|
|
int index = 0;
|
|
#ifdef UNICODE
|
|
int size = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
|
|
wchar_t * wszname = (wchar_t *)malloc(size * sizeof(wchar_t));
|
|
if (wszname)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, name, -1, wszname, size);
|
|
}
|
|
#endif
|
|
|
|
CLock lock(m_pDevNumsMutes);
|
|
|
|
m_nDevNums = 0;
|
|
|
|
DirectSoundCaptureEnumerate(DevEnumCallback, NULL);
|
|
|
|
for (int i = 0; i < m_nDevNums; i++)
|
|
{
|
|
DSOUND_DEVICE * p_device = &CWAudioCapture::m_devices[i];
|
|
|
|
#ifdef UNICODE
|
|
if (wszname != NULL && wcsicmp(p_device->desc, wszname) == 0)
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
#else
|
|
if (strcasecmp(p_device->desc, name) == 0)
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
if (wszname)
|
|
{
|
|
free(wszname);
|
|
}
|
|
#endif
|
|
|
|
return index;
|
|
}
|
|
|
|
BOOL CWAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
|
|
{
|
|
CLock lock(m_pMutex);
|
|
|
|
if (m_bInited)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (m_nDevIndex == DEF_AUDIO_CAP_DEV)
|
|
{
|
|
}
|
|
else if (m_nDevIndex < 0 || m_nDevIndex >= getDeviceNums())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT ret;
|
|
|
|
if (DEF_AUDIO_CAP_DEV == m_nDevIndex)
|
|
{
|
|
ret = DirectSoundCaptureCreate8(NULL, &m_pDSound8, NULL);
|
|
}
|
|
else
|
|
{
|
|
DSOUND_DEVICE * p_device = &CWAudioCapture::m_devices[m_nDevIndex];
|
|
|
|
ret = DirectSoundCaptureCreate8(&p_device->guid, &m_pDSound8, NULL);
|
|
}
|
|
|
|
if (FAILED(ret))
|
|
{
|
|
log_print(HT_LOG_ERR, "%s, audio captrue create failed\r\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
WAVEFORMATEX format;
|
|
memset(&format, 0, sizeof(WAVEFORMATEX));
|
|
|
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
format.nChannels = 2;
|
|
format.wBitsPerSample = 16;
|
|
format.nSamplesPerSec = samplerate;
|
|
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
|
|
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec;
|
|
format.cbSize = 0;
|
|
|
|
DSCBUFFERDESC buf_desc;
|
|
memset(&buf_desc, 0, sizeof(buf_desc));
|
|
|
|
buf_desc.dwSize = sizeof(buf_desc);
|
|
buf_desc.dwFlags = DSCBCAPS_WAVEMAPPED;
|
|
buf_desc.dwBufferBytes = HTAUDIOCBUF_MAX;
|
|
buf_desc.dwReserved = 0;
|
|
buf_desc.lpwfxFormat = &format;
|
|
|
|
ret = m_pDSound8->CreateCaptureBuffer(&buf_desc, &m_pDSoundBuffer, NULL);
|
|
if (ret != DS_OK)
|
|
{
|
|
log_print(HT_LOG_ERR, "%s, audio captrue create buffer failed\r\n", __FUNCTION__);
|
|
|
|
stopCapture();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
AudioEncoderParam params;
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
params.SrcChannels = 2;
|
|
params.SrcSamplefmt = AV_SAMPLE_FMT_S16;
|
|
params.SrcSamplerate = samplerate;
|
|
params.DstChannels = channels;
|
|
params.DstSamplefmt = AV_SAMPLE_FMT_S16;
|
|
params.DstSamplerate = samplerate;
|
|
params.DstBitrate = bitrate;
|
|
params.DstCodec = codec;
|
|
|
|
if (m_encoder.init(¶ms) == FALSE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
m_nChannels = channels;
|
|
m_nSampleRate = samplerate;
|
|
m_nBitrate = bitrate;
|
|
m_bInited = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CWAudioCapture::startCapture()
|
|
{
|
|
CLock lock(m_pMutex);
|
|
|
|
if (!m_bInited)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_bCapture)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT ret = m_pDSoundBuffer->Start(DSCBSTART_LOOPING);
|
|
if (ret != DS_OK)
|
|
{
|
|
log_print(HT_LOG_ERR, "%s, audio captrue start failed\r\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
m_bCapture = TRUE;
|
|
m_hCapture = sys_os_create_thread((void *)audioCaptureThread, this);
|
|
|
|
return (m_hCapture ? TRUE : FALSE);
|
|
}
|
|
|
|
void CWAudioCapture::stopCapture(void)
|
|
{
|
|
m_bCapture = FALSE;
|
|
|
|
while (m_hCapture)
|
|
{
|
|
usleep(10*1000);
|
|
}
|
|
|
|
if (m_pDSoundBuffer)
|
|
{
|
|
m_pDSoundBuffer->Stop();
|
|
m_pDSoundBuffer->Release();
|
|
m_pDSoundBuffer = NULL;
|
|
}
|
|
|
|
if (m_pDSound8)
|
|
{
|
|
m_pDSound8->Release();
|
|
m_pDSound8 = NULL;
|
|
}
|
|
|
|
m_bInited = FALSE;
|
|
}
|
|
|
|
BOOL CWAudioCapture::capture()
|
|
{
|
|
int specsize = HTAUDIOC_CHNUMS * HTAUDIOC_BPS * HTAUDIOC_SAMPLE;
|
|
DWORD junk, cursor, ptr1len, ptr2len;
|
|
void *ptr1, *ptr2;
|
|
|
|
while (m_bCapture)
|
|
{
|
|
if (m_pDSoundBuffer->GetCurrentPosition(&junk, &cursor) != DS_OK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ((cursor / specsize) == m_nLastChunk)
|
|
{
|
|
usleep(10*1000);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_pDSoundBuffer->Lock(m_nLastChunk * specsize, specsize, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
assert(ptr1len == specsize);
|
|
assert(ptr2 == NULL);
|
|
assert(ptr2len == 0);
|
|
|
|
m_encoder.encode((uint8*)ptr1, ptr1len);
|
|
|
|
if (m_pDSoundBuffer->Unlock(ptr1, ptr1len, ptr2, ptr2len) != DS_OK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
m_nLastChunk = (m_nLastChunk + 1) % HTAUDIOC_CHUNKS;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CWAudioCapture::captureThread()
|
|
{
|
|
int samplerate = m_nSampleRate;
|
|
int64 cur_delay = 0;
|
|
int64 pre_delay = 0;
|
|
uint32 cur_time = 0;
|
|
uint32 pre_time = 0;
|
|
|
|
while (m_bCapture)
|
|
{
|
|
if (capture())
|
|
{
|
|
cur_time = sys_os_get_ms();
|
|
cur_delay = 1000000.0 / samplerate * HTAUDIOC_SAMPLE;
|
|
|
|
if (pre_time > 0)
|
|
{
|
|
cur_delay += pre_delay - (cur_time - pre_time) * 1000;
|
|
if (cur_delay < 1000)
|
|
{
|
|
cur_delay = 0;
|
|
}
|
|
}
|
|
|
|
pre_time = cur_time;
|
|
pre_delay = cur_delay;
|
|
|
|
if (cur_delay > 0)
|
|
{
|
|
usleep(cur_delay);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
usleep(10*1000);
|
|
}
|
|
}
|
|
|
|
m_hCapture = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|