gf_ai_box/include/modules/apps/uac/uac_service.cpp
2026-04-12 17:47:54 +08:00

414 lines
11 KiB
C++

/*
*******************************************************************************
* Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
*
* +-----------------------------------------------------------------+
* | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
* | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH |
* | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
* | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE |
* | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE |
* | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED. |
* | |
* | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT |
* | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
* | VATICS(KNERON) INC. |
* +-----------------------------------------------------------------+
*
*******************************************************************************
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string>
#include <poll.h>
#include <SyncRingBuffer/sync_ring_buffer.h>
#include <audiotk/audio_capture_mmap.h>
#include <audiotk/audio_playback_mmap.h>
#include <audiotk/audio_vol_ctrl.h>
#define SRB_HEADER_SIZE 4
#define ATK_AUDIO_MAX_CHANNELS 32
#define PERIOD_SIZE_IN_FRAMES 1024
#define APP_NAME "UAC Service"
#define CAP_DIR "./"
#define UNUSED(x) (void)(x)
enum {
FALSE = 0,
TRUE,
};
//Playback Handle
ATK_AUDIOPLAY_HANDLE_T* playback_handle;
//Capture Handle
ATK_AUDIOCAP_HANDLE_T* capture_handle;
static int isRunning = 0;
static int reset = 0;
static int savefile = 0;
char const *file_name = NULL;
char filename[128];
FILE* fp;
srb_handle_t* reader_handle = NULL;
srb_handle_t* writer_handle = NULL;
srb_buffer_t srb_writer_buf;
srb_buffer_t srb_reader_buf;
//======================================
//HELP
static void print_usage(const char *ap_name)
{
fprintf(stderr, "Usage:\n"
" %s [-i input_type] [-m][-h]\n"
"Options:\n"
" -r Sample rate, it is applied to both capture and playback\n"
" -i Input Selection 0:Mic, 1:LineIn (0: default)\n"
" -s save recording file with filename 0:Don't save 1:Save (0:default)\n"
" -f filename Required if using -s option\n "
" -t Select if want to play interlevead audio 1: interleaved 0: no interleaved default :0\n "
" -c number of channels: 1: 1 channel 2: 2 channels \n "
" -o simple configuration 1: simple conf on 0: simple config off, defualt value 0\n "
" -p periods contained in one buffer. default value 8 \n "
" -d Sound device 0:Normal, 1:USB (0: default)\n "
" -h This help.\n", ap_name);
}
//======================================
//write to fp
static void write_to_file(unsigned char *data_buf, size_t buf_size)
{
fwrite(data_buf, buf_size, 1, fp);
fflush(fp);
}
//======================================
//capture thread / SRB writer
static void audiocap_callback(const ATK_AUDIO_NOTIFY_DATA_INFO_T *audio_info, void* user_data)
{
UNUSED(user_data);
if(!isRunning)
return ;
if(audio_info->dwDataBytes > 0) {
if (savefile){
//append data to a file
write_to_file(audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes);
}
unsigned int* buf_ptr = (unsigned int*)srb_writer_buf.buffer;
buf_ptr[0] = audio_info->dwDataBytes;
memcpy(srb_writer_buf.buffer + SRB_HEADER_SIZE, audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes);
SRB_SendGetWriterBuff(writer_handle, &srb_writer_buf);
}
else {
printf("[%s,%d] sending data to client failed, send bytes: %d, terminating ...\n", __func__,__LINE__, audio_info->dwDataBytes);
isRunning = false;
}
}
//playback buff
void* srb_reader(void *ptr __attribute__((unused)))
{
if (!playback_handle){
printf(" [%s,%d] playback handle init error \n",__func__,__LINE__);
return NULL;
}
// Audio output buffer.
unsigned char *pbyBufs[ATK_AUDIO_MAX_CHANNELS] = {NULL};
unsigned int dwDataBytes;
while(isRunning && !reset){
if(ATK_AudioPlay_PlayPeriodFrames(playback_handle, pbyBufs) < 0){
fprintf(stderr, "[%s,%s] Can't get the audio output buffer..\n", __FILE__, __func__);
isRunning = false;
break;
}
if(SRB_ReturnReceiveReaderBuff(reader_handle, &srb_reader_buf)==0){
unsigned int* buf_ptr = (unsigned int*)(srb_reader_buf.buffer);
dwDataBytes = buf_ptr[0];
memcpy(pbyBufs[0],srb_reader_buf.buffer + SRB_HEADER_SIZE,dwDataBytes);
}
}
return NULL;
}
static void sig_kill(int signo)
{
printf("[%s,%d] Received SIGNAL %d!!!\n", __func__,__LINE__, signo);
switch(signo){
case SIGTERM:
case SIGINT:
isRunning = false;
SRB_WakeupReader(reader_handle);
break;
default:
break;
}
}
//======================================
int main(int argc, char* argv[])
{
int ret;
int opt = -1;
int input = 0; //Mic
unsigned int interleaved = 1; // is playing interleaved audio
unsigned int sample_rate = 8000; // audio sample rate
unsigned int channels = 2 ; //number of channels
int simple_conf = 0; //use_simple_config
unsigned int periods = 8; //periods_per_buffer
unsigned int device = 0; //sound device number 0 normal ; 1 usb
const char card[] = "hw:0,0";
char as_on[20] = {'0'};
signed short fd_as;
pthread_t tid = 0;
while ((opt = getopt(argc, argv, "i:r:s:f:t:c:o:p:d:")) != -1){
switch(opt){
case 'r':
sample_rate = atoi(optarg);
break;
case 'i': //Input selection 0 MicIn / 1 LineIn
input = atoi(optarg);//strdup(optarg);
break;
case 's':
savefile = atoi(optarg);
break;
case 'f':
file_name = strdup(optarg);
break;
case 't':
interleaved = atoi(optarg);
break;
case 'c':
channels = atoi(optarg);
break;
case 'o':
simple_conf = atoi(optarg);
break;
case 'p':
periods = atoi(optarg);
break;
case 'd':
device = atoi(optarg);
break;
default:
print_usage(APP_NAME);
goto end;
break;
}
}
if (savefile && NULL == file_name){
file_name = "audio_lookback.wav";
}
if (input > 1 || input < 0){
printf(" [%s,%d] input value is either 0=Mic or 1=LineIn \n",__func__,__LINE__);
goto end;
} else{
ret = input? ATK_Audio_InputSelection(kTKAudioLineIn):ATK_Audio_InputSelection(kTKAudioMicIn);
if (ret){
printf(" [%s,%d] input selection error \n",__func__,__LINE__);
goto end;
}
}
if (channels > 2){
printf(" [%s,%d] invalid channels number... Using default value! \n",__func__,__LINE__);
channels = 2;
}
//open FD to get sample rate
fd_as = open("/sys/class/webcam/uac/as_stream",O_RDONLY);
if(fd_as < 0){
printf("Cannot read sample rate form UAC \n");
goto end;
}
ATK_AUDIOCAP_CONFIG_T capture_config; //Capture
ATK_AUDIOPLAY_CONFIG_T playback_config;//Playback
//initial configuration
memset(&playback_config,0,sizeof(ATK_AUDIOPLAY_CONFIG_T));
memset(&capture_config,0,sizeof(ATK_AUDIOCAP_CONFIG_T));
//Playback
sprintf((char*)card,"hw:%d,0",device);
playback_config.szPcmName = card;
playback_config.bIsInterleaved = interleaved;
playback_config.eFormat = SND_PCM_FORMAT_S16_LE;
playback_config.dwChannelsCount = channels;
playback_config.dwSampleRate = sample_rate;
playback_config.bUseSimpleConfig = simple_conf;
playback_config.dwPeriodsPerBuffer = periods;
playback_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
playback_config.bDropFramesBeforeStop = TRUE;
//Capture
capture_config.szPcmName = "hw:0,0";
capture_config.bIsInterleaved = interleaved;
capture_config.eFormat = SND_PCM_FORMAT_S16_LE;
capture_config.dwChannelsCount = channels;
capture_config.dwSampleRate = sample_rate;
capture_config.bUseSimpleConfig = simple_conf;
capture_config.dwPeriodsPerBuffer = periods;
capture_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES;
capture_config.pfnCallback = audiocap_callback;
capture_config.pUserData = NULL;
//save audio to file...
if(savefile){
snprintf(filename, sizeof(filename), CAP_DIR "%s", file_name);
fp = fopen(filename, "ab");
}
signal(SIGPIPE, SIG_IGN);
signal(SIGTERM, sig_kill);
signal(SIGINT, sig_kill);
isRunning = true;
while(isRunning){
while (as_on[0] == '0' || reset == 0)
{
struct pollfd p;
int ret;
#if 1
p.fd = fd_as;
p.events = POLLERR | POLLPRI;
p.revents = 0;
ret = poll(&p,1,-1);
if (ret<0)
printf("Error poll failed\n");
else if (ret == 0)
printf("Error poll timeout \n");
else if (p.revents & (POLLERR | POLLPRI)) {
if((ret = pread(fd_as,&as_on,sizeof(as_on),0))<0) {
printf("error poll read failed \n");
}else{
reset = 1;
if (as_on[0] == '0') {
continue;
}
}
}
#else
fd_set efds;
FD_ZERO(&efds);
FD_SET(fd_as, &efds);
ret = select(fd_as + 1, NULL, NULL, &efds, NULL);
if (-1 == ret) {
printf("select error %d, %s\n",
errno, strerror (errno));
if (EINTR == errno)
continue;
}
if (FD_ISSET(fd_as, &efds)) {
if((ret = pread(fd_as,&as_on,sizeof(as_on),0))<0) {
printf("error read failed \n");
}else{
reset = 1;
if (as_on[0] == '0')
continue;
}
}
#endif
if (tid != 0) {
SRB_WakeupReader(reader_handle);
pthread_join(tid, NULL);
tid = 0;
}
//release playabck and capture change state to 0
if (capture_handle)
ATK_AudioCap_Release(capture_handle);
if (playback_handle)
ATK_AudioPlay_Release(playback_handle);
//release srb handles
if(writer_handle)
SRB_Release(writer_handle);
if(reader_handle)
SRB_Release(reader_handle);
}
if(reset == 1)
{
sample_rate = atoi(as_on);
playback_config.dwSampleRate = sample_rate;
capture_config.dwSampleRate = sample_rate;
reset = 0;
writer_handle = SRB_InitWriter("srb_uac", MAX_RING_BUF_SIZE, 4);
if (!writer_handle){
printf(" [%s,%d] srb writer handle init error \n",__func__,__LINE__);
isRunning = false;
break;
}
SRB_SendGetWriterBuff(writer_handle, &srb_writer_buf);
reader_handle = SRB_InitReader("srb_uac");
if (!reader_handle){
printf(" [%s,%d] srb reader handle init error \n",__func__,__LINE__);
isRunning = false;
break;
}
capture_handle = ATK_AudioCap_Init(&capture_config);
if(!capture_handle){
printf(" [%s,%d] ATK_AudioCap_Init error \n",__func__,__LINE__);
isRunning = false;
break;
}
playback_handle = ATK_AudioPlay_Init(&playback_config);
if(!playback_handle){
printf(" [%s,%d] ATK_AudioPlay_Init error \n",__func__,__LINE__);
isRunning = false;
break;
}
pthread_create(&tid, NULL, srb_reader, NULL);
printf(" [%s,%d] pthread_create tid = %lu sample_rate = %d\n",__func__,__LINE__,tid, sample_rate);
}
}
isRunning = false;
pthread_join(tid, NULL);
printf(" [%s,%d] Leaving the program \n",__func__,__LINE__);
end:
if(savefile)
fclose(fp);
if(NULL!=writer_handle)
SRB_Release(writer_handle);
if(NULL!=reader_handle)
SRB_Release(reader_handle);
if(NULL!=playback_handle)
ATK_AudioPlay_Release(playback_handle);
if(NULL!=capture_handle)
ATK_AudioCap_Release(capture_handle);
}