414 lines
11 KiB
C++
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);
|
|
|
|
}
|