/*************************************************************************************** * * 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 /***************************************************************************************/ #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; }