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