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