426 lines
8.5 KiB
C++
426 lines
8.5 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-2024, 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_linux.h"
|
||
|
|
#include "lock.h"
|
||
|
|
|
||
|
|
|
||
|
|
/***************************************************************************************/
|
||
|
|
static void * audioCaptureThread(void * argv)
|
||
|
|
{
|
||
|
|
CLAudioCapture *capture = (CLAudioCapture *)argv;
|
||
|
|
|
||
|
|
capture->captureThread();
|
||
|
|
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/***************************************************************************************/
|
||
|
|
|
||
|
|
CLAudioCapture::CLAudioCapture() : CAudioCapture()
|
||
|
|
{
|
||
|
|
memset(&m_alsa, 0, sizeof(m_alsa));
|
||
|
|
|
||
|
|
m_alsa.period_size = 64;
|
||
|
|
m_alsa.framesize = 16 / 8 * 2;
|
||
|
|
|
||
|
|
m_pData = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
CLAudioCapture::~CLAudioCapture()
|
||
|
|
{
|
||
|
|
stopCapture();
|
||
|
|
}
|
||
|
|
|
||
|
|
CAudioCapture * CLAudioCapture::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 CLAudioCapture();
|
||
|
|
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 CLAudioCapture::getDeviceNums()
|
||
|
|
{
|
||
|
|
int card = -1, count = 0, ret;
|
||
|
|
snd_ctl_card_info_t * info;
|
||
|
|
|
||
|
|
snd_ctl_card_info_alloca(&info);
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
|
||
|
|
while (ret == 0 && card != -1)
|
||
|
|
{
|
||
|
|
char name[16];
|
||
|
|
snd_ctl_t * ctl;
|
||
|
|
|
||
|
|
snprintf(name, sizeof(name), "hw:%d", card);
|
||
|
|
|
||
|
|
if (snd_ctl_open(&ctl, name, SND_CTL_NONBLOCK) < 0)
|
||
|
|
{
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (snd_ctl_card_info(ctl, info) < 0)
|
||
|
|
{
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
|
||
|
|
if (++count >= MAX_AUDIO_DEV_NUMS)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
}
|
||
|
|
|
||
|
|
return count > MAX_AUDIO_DEV_NUMS ? MAX_AUDIO_DEV_NUMS : count;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CLAudioCapture::listDevice()
|
||
|
|
{
|
||
|
|
int card = -1, count = 0, ret;
|
||
|
|
snd_ctl_card_info_t * info;
|
||
|
|
|
||
|
|
printf("\r\nAvailable audio capture device : \r\n\r\n");
|
||
|
|
|
||
|
|
snd_ctl_card_info_alloca(&info);
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
|
||
|
|
while (ret == 0 && card != -1)
|
||
|
|
{
|
||
|
|
char name[16];
|
||
|
|
snd_ctl_t * ctl;
|
||
|
|
|
||
|
|
snprintf(name, sizeof(name), "hw:%d", card);
|
||
|
|
|
||
|
|
if (snd_ctl_open(&ctl, name, SND_CTL_NONBLOCK) < 0)
|
||
|
|
{
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (snd_ctl_card_info(ctl, info) < 0)
|
||
|
|
{
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
printf("index : %d, name : %s\r\n", count, snd_ctl_card_info_get_name(info));
|
||
|
|
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
|
||
|
|
if (++count >= MAX_AUDIO_DEV_NUMS)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int CLAudioCapture::getDeviceIndex(const char * name)
|
||
|
|
{
|
||
|
|
int card = -1, count = 0, index = 0, ret;
|
||
|
|
snd_ctl_card_info_t * info;
|
||
|
|
|
||
|
|
snd_ctl_card_info_alloca(&info);
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
|
||
|
|
while (ret == 0 && card != -1)
|
||
|
|
{
|
||
|
|
char cname[16];
|
||
|
|
snd_ctl_t * ctl;
|
||
|
|
|
||
|
|
snprintf(cname, sizeof(cname), "hw:%d", card);
|
||
|
|
|
||
|
|
if (snd_ctl_open(&ctl, cname, SND_CTL_NONBLOCK) < 0)
|
||
|
|
{
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (snd_ctl_card_info(ctl, info) < 0)
|
||
|
|
{
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (strcasecmp(snd_ctl_card_info_get_name(info), name) == 0)
|
||
|
|
{
|
||
|
|
index = count;
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
|
||
|
|
if (++count >= MAX_AUDIO_DEV_NUMS)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
}
|
||
|
|
|
||
|
|
return index;
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CLAudioCapture::getDeviceName(int index, char * name, int namesize)
|
||
|
|
{
|
||
|
|
BOOL res = FALSE;
|
||
|
|
int card = -1, count = 0, ret;
|
||
|
|
snd_ctl_card_info_t * info;
|
||
|
|
|
||
|
|
snd_ctl_card_info_alloca(&info);
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
|
||
|
|
while (ret == 0 && card != -1)
|
||
|
|
{
|
||
|
|
char cname[16];
|
||
|
|
snd_ctl_t * ctl;
|
||
|
|
|
||
|
|
snprintf(cname, sizeof(cname), "hw:%d", card);
|
||
|
|
|
||
|
|
if (snd_ctl_open(&ctl, cname, SND_CTL_NONBLOCK) < 0)
|
||
|
|
{
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (snd_ctl_card_info(ctl, info) < 0)
|
||
|
|
{
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (index == count)
|
||
|
|
{
|
||
|
|
res = TRUE;
|
||
|
|
strncpy(name, snd_ctl_card_info_get_name(info), namesize);
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
snd_ctl_close(ctl);
|
||
|
|
|
||
|
|
if (++count >= MAX_AUDIO_DEV_NUMS)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = snd_card_next(&card);
|
||
|
|
}
|
||
|
|
|
||
|
|
return res;
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CLAudioCapture::initCapture(int codec, int samplerate, int channels, int bitrate)
|
||
|
|
{
|
||
|
|
CLock lock(m_pMutex);
|
||
|
|
|
||
|
|
if (m_bInited)
|
||
|
|
{
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (alsa_get_device_name(m_nDevIndex, m_alsa.devname, sizeof(m_alsa.devname)) == FALSE)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "get device name failed. %d\r\n", m_nDevIndex);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (alsa_open_device(&m_alsa, SND_PCM_STREAM_CAPTURE, (uint32 *)&samplerate, 2) == FALSE)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "open device (%s) failed\r\n", m_alsa.devname);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pData = (uint8 *)malloc(ALSA_BUFFER_SIZE_MAX);
|
||
|
|
if (NULL == m_pData)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
memset(m_pData, 0, ALSA_BUFFER_SIZE_MAX);
|
||
|
|
|
||
|
|
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 CLAudioCapture::capture()
|
||
|
|
{
|
||
|
|
int res = 0;
|
||
|
|
|
||
|
|
if (NULL == m_alsa.handler)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (m_bCapture)
|
||
|
|
{
|
||
|
|
res = snd_pcm_wait(m_alsa.handler, 100);
|
||
|
|
if (res < 0)
|
||
|
|
{
|
||
|
|
if (alsa_xrun_recover(m_alsa.handler, res) < 0)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
res = snd_pcm_readi(m_alsa.handler, m_pData, m_alsa.period_size);
|
||
|
|
if (res == -EAGAIN)
|
||
|
|
{
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
else if (res < 0)
|
||
|
|
{
|
||
|
|
if (alsa_xrun_recover(m_alsa.handler, res) < 0)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (res > 0)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return m_encoder.encode(m_pData, res * m_alsa.framesize);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CLAudioCapture::captureThread()
|
||
|
|
{
|
||
|
|
while (m_bCapture)
|
||
|
|
{
|
||
|
|
if (capture())
|
||
|
|
{
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
usleep(10*1000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_hCapture = 0;
|
||
|
|
|
||
|
|
log_print(HT_LOG_INFO, "%s, exit\r\n", __FUNCTION__);
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CLAudioCapture::startCapture()
|
||
|
|
{
|
||
|
|
CLock lock(m_pMutex);
|
||
|
|
|
||
|
|
if (!m_bInited)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_bCapture)
|
||
|
|
{
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_bCapture = TRUE;
|
||
|
|
m_hCapture = sys_os_create_thread((void *)audioCaptureThread, this);
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CLAudioCapture::stopCapture(void)
|
||
|
|
{
|
||
|
|
CLock lock(m_pMutex);
|
||
|
|
|
||
|
|
m_bCapture = FALSE;
|
||
|
|
|
||
|
|
while (m_hCapture)
|
||
|
|
{
|
||
|
|
usleep(10*1000);
|
||
|
|
}
|
||
|
|
|
||
|
|
alsa_close_device(&m_alsa);
|
||
|
|
|
||
|
|
if (m_pData)
|
||
|
|
{
|
||
|
|
free(m_pData);
|
||
|
|
m_pData = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_bInited = FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|