| /***************************************************************************** | |
| ** °æÈ¨ËùÓÐ (C)2015, ÖÐÐËͨѶ¹É·ÝÓÐÏÞ¹«Ë¾¡£ | |
| ** | |
| ** ÎļþÃû³Æ: voice_buffer.c | |
| ** Îļþ±êʶ: | |
| ** ÄÚÈÝÕªÒª: | |
| ** ʹÓ÷½·¨: | |
| ** | |
| ** ÐÞ¸ÄÈÕÆÚ °æ±¾ºÅ Ð޸ıê¼Ç ÐÞ¸ÄÈË ÐÞ¸ÄÄÚÈÝ | |
| ** ----------------------------------------------------------------------------- | |
| ** 2023/07/18 V1.0 Create xxq ´´½¨ | |
| ** | |
| * ******************************************************************************/ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <assert.h> | |
| #include <errno.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <stdint.h> | |
| #include <fcntl.h> | |
| #include <sys/ioctl.h> | |
| #include <signal.h> | |
| #include <poll.h> | |
| #include <pthread.h> | |
| #include <semaphore.h> | |
| #include <linux/volte_drv.h> | |
| //#define VB_DATA_LOSS_TEST | |
| #define VB_MAX_INT 0x7fffffff | |
| #define VB_MIN_INT 0 //(0-2<<31)// | |
| #define VB_INT_OVERFLOW(x) if((x < VB_MIN_INT)||(x > VB_MAX_INT)) x = 0; | |
| #define _TX_RX_DATA_FROM_FILE //test use | |
| #define VB_FUNC_DEBUG //DEBUG use | |
| #define VBUFFER_DEV_NAME "/dev/voice_buffer_dev" | |
| #define VBUFFER_DEV_FLAGS O_RDWR //O_RDWR | O_SYNC | |
| typedef int (vb_thread_proc)(void*); | |
| typedef int (vb_ext_rx_tx_func)(void*,int); | |
| struct vb_info_t | |
| { | |
| int fd; | |
| pthread_t rx_thread; | |
| pthread_t tx_thread; | |
| int quit; | |
| char *tx_buf; | |
| char *rx_buf; | |
| int buf_size; | |
| vb_ext_rx_tx_func *ext_rx_func; | |
| vb_ext_rx_tx_func *ext_tx_func; | |
| sem_t read_sem; | |
| sem_t write_sem; | |
| #ifdef _TX_RX_DATA_FROM_FILE | |
| char *tx_filename; | |
| char *rx_filename; | |
| FILE *tx_file; | |
| FILE *rx_file; | |
| int tx_filesize; | |
| int rx_filesize; | |
| #endif | |
| }; | |
| struct vb_info_t vb_rec = {0}; | |
| int tx_optcount = 0; | |
| int rx_optcount = 0; | |
| int first_rderr_flag = 0; | |
| int first_wrerr_flag = 0; | |
| void vb_signal_back_func(int signum) | |
| { | |
| sem_post(&vb_rec.read_sem); | |
| sem_post(&vb_rec.write_sem); | |
| } | |
| int voice_buffer_open(void) | |
| { | |
| int fd; | |
| int oflags; | |
| fd = open(VBUFFER_DEV_NAME, VBUFFER_DEV_FLAGS); | |
| printf("%s: fd=%d!\n", __func__, fd); | |
| signal(SIGIO, vb_signal_back_func); //Ó¦ÓóÌÐò×¢²áÐźŴ¦Àíº¯Êý£¬SIGIO±íʾIOÓÐÊý¾Ý¿É¹©¶ÁÈ¡ | |
| fcntl(fd, F_SETOWN, getpid()); | |
| oflags = fcntl(fd, F_GETFL); | |
| fcntl(fd, F_SETFL, oflags|FASYNC); | |
| sem_init(&vb_rec.read_sem, 0, 0); | |
| sem_init(&vb_rec.write_sem, 0, 0); | |
| tx_optcount = 0; | |
| rx_optcount = 0; | |
| first_rderr_flag = 0; | |
| first_wrerr_flag = 0; | |
| return fd; | |
| } | |
| int voice_buffer_close(int fd) | |
| { | |
| int ret = 0; | |
| if(fd >= 0){ | |
| ret = close(fd); | |
| } | |
| else{ | |
| printf("%s: fd invalid, return!\n", __func__); | |
| return -ENOENT; | |
| } | |
| vb_rec.fd = -1; | |
| sem_destroy(&vb_rec.read_sem); | |
| sem_destroy(&vb_rec.write_sem); | |
| printf("%s, first_rderr_flag=%d, first_wrerr_flag=%d, tx_optcount=%d, rx_optcount=%d\n", __func__, first_rderr_flag, first_wrerr_flag, tx_optcount, rx_optcount); | |
| return ret; | |
| } | |
| int voice_buffer_write(int fd, unsigned char *pdata, unsigned int len) | |
| { | |
| int ret; | |
| static struct timespec pre_wtime = {0}; | |
| struct timespec cur_wtime = {0}; | |
| if(fd >= 0){ | |
| sem_wait(&vb_rec.write_sem); | |
| clock_gettime(CLOCK_REALTIME, &cur_wtime); | |
| //printf("%s: tv_sec=%d, tv_nsec=%d\r\n", __func__, cur_wtime.tv_sec, cur_wtime.tv_nsec); | |
| ret = write(fd, pdata, len); | |
| } | |
| else{ | |
| printf("%s: fd invalid, return!\n",__func__); | |
| return -ENOENT; | |
| } | |
| return ret; | |
| } | |
| int voice_buffer_read(int fd, unsigned char *pdata, unsigned int len) | |
| { | |
| int ret; | |
| static struct timespec pre_rtime = {0}; | |
| struct timespec cur_rtime = {0}; | |
| if(fd >= 0){ | |
| sem_wait(&vb_rec.read_sem); | |
| clock_gettime(CLOCK_REALTIME, &cur_rtime); | |
| //printf("%s: tv_sec=%d, tv_nsec=%d\r\n", __func__, cur_rtime.tv_sec, cur_rtime.tv_nsec); | |
| ret = read(fd, pdata, len); | |
| } | |
| else{ | |
| printf("%s: fd invalid, return!\n", __func__); | |
| return -ENOENT; | |
| } | |
| return ret; | |
| } | |
| int voice_buffer_ioctl(int fd, unsigned int cmd, void *pvalue) | |
| { | |
| int ret = 0; | |
| if(fd >= 0){ | |
| ret = ioctl(fd, cmd, pvalue); | |
| } | |
| else{ | |
| printf("%s: fd invalid, return\n", __func__); | |
| return -ENOENT; | |
| } | |
| return ret; | |
| } | |
| static int vb_rx_thread_func(void *arg) | |
| { | |
| int ret = 0; | |
| char* buf = vb_rec.rx_buf; | |
| int size = vb_rec.buf_size; | |
| unsigned int bytes_read = 0; | |
| int r_size = 0; | |
| printf("%s: start size=%d!\n", __func__, size); | |
| memset(buf, 0, size); | |
| while(!vb_rec.quit){ | |
| rx_optcount ++; | |
| VB_INT_OVERFLOW(rx_optcount); | |
| if((rx_optcount % 1000) == 0){ | |
| printf("%s: rx_optcount=%d!\n", __func__, rx_optcount); | |
| if(rx_optcount == 1000000) | |
| rx_optcount = 0; | |
| } | |
| //read from ps | |
| r_size = voice_buffer_read(vb_rec.fd, buf, size); | |
| if(r_size <= 0){ | |
| first_rderr_flag++; | |
| VB_INT_OVERFLOW(first_rderr_flag); | |
| if(first_rderr_flag == 1){ | |
| printf( "%s: error reading data!\n", __func__); | |
| } | |
| if((first_rderr_flag % 500) == 0){ | |
| printf("%s: first_rderr_flag=%d!\n", __func__, first_rderr_flag); | |
| } | |
| continue ; | |
| } | |
| else{ | |
| first_rderr_flag = 0; | |
| } | |
| #ifdef _TX_RX_DATA_FROM_FILE | |
| if(vb_rec.rx_file != NULL) | |
| { | |
| #if 0 | |
| static int i=0; | |
| i++; | |
| if (i==50) { | |
| printf("vb_rx_thread_func buf[%d]=%d\n",i,buf[i]); | |
| i=0; | |
| } | |
| #endif | |
| //if(fwrite(buf, 1, size, vb_rec.rx_file) != size){//µ¥¸ö×Ö½ÚдÈ룬ÎļþûÊý¾Ý | |
| if(fwrite(buf, size, 1, vb_rec.rx_file) != 1){ | |
| printf("%s: Error fwrite\n", __func__); | |
| //break; | |
| } | |
| else{ | |
| bytes_read += size; | |
| if(bytes_read >= vb_rec.rx_filesize){ | |
| //fseek(vb_rec.rx_file, 0, SEEK_SET);//дÈë²»ÏÞÖÆÎļþ´óС | |
| } | |
| } | |
| } | |
| #endif | |
| } | |
| return 0; | |
| } | |
| static int vb_tx_thread_func(void *arg) | |
| { | |
| int ret = 0; | |
| int num_read = 0; | |
| char* buf = vb_rec.tx_buf; | |
| int size = vb_rec.buf_size; | |
| int w_size = 0; | |
| printf("%s: start size=%d!\n", __func__, size); | |
| #ifndef VB_DATA_LOSS_TEST | |
| memset(buf, 0, size); | |
| #endif | |
| while(!vb_rec.quit){ | |
| #if defined (_TX_RX_DATA_FROM_FILE) && !defined (VB_DATA_LOSS_TEST) | |
| if(vb_rec.tx_file != NULL) | |
| { | |
| //num_read = fread(buf, 1, size, vb_rec.tx_file); | |
| num_read = fread(buf, size, 1, vb_rec.tx_file); | |
| if (num_read <= 0) { | |
| printf("%s: vb_rec.tx_file read end\n",__func__); | |
| // no data | |
| //fseek(vb_rec.tx_file, 0, SEEK_SET); | |
| } | |
| } | |
| #endif | |
| tx_optcount ++; | |
| VB_INT_OVERFLOW(tx_optcount); | |
| if((tx_optcount%1000) == 0){ | |
| printf("%s: tx_optcount=%d!\n", __func__, tx_optcount); | |
| if(tx_optcount == 1000000) | |
| tx_optcount = 0; | |
| } | |
| //write to ps | |
| w_size = voice_buffer_write(vb_rec.fd, buf, size); | |
| if(w_size <= 0){ | |
| first_wrerr_flag++; | |
| VB_INT_OVERFLOW(first_wrerr_flag); | |
| if(first_wrerr_flag ==1){ | |
| printf("%s: error writing data!\n", __func__); | |
| } | |
| if((first_wrerr_flag % 500) == 0){ | |
| printf("%s: first_wrerr_flag=%d!\n", __func__, first_wrerr_flag); | |
| } | |
| continue; | |
| } | |
| else{ | |
| first_wrerr_flag = 0; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int vb_thread_create( const char *name,pthread_t *thread_t, vb_thread_proc *proc, | |
| int stack_size, unsigned priority,void *arg ) | |
| { | |
| pthread_attr_t thread_attr; | |
| int ret = 0; | |
| int default_size = 0; | |
| struct sched_param param; | |
| int policy = SCHED_FIFO; | |
| /* Set default stack size */ | |
| //stack_size = 8*1024;//32*1024; | |
| printf("%s: start!\n", __func__); | |
| /* Init thread attributes */ | |
| pthread_attr_init(&thread_attr); | |
| //pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); | |
| /* | |
| ret = pthread_attr_setstacksize(&thread_attr, stack_size); | |
| if (ret != 0) | |
| { | |
| printf("%s: pthread_attr_setstacksize(%d) fail,ret=%d! \n",__func__,stack_size,ret); | |
| pthread_attr_destroy(&thread_attr); | |
| return ret; | |
| } | |
| */ | |
| /* Create the thread. */ | |
| ret = pthread_create( thread_t, &thread_attr,proc, arg); | |
| if(ret != 0){ | |
| printf("%s: pthread_create fail,ret=%d!\n", __func__, ret); | |
| pthread_attr_destroy(&thread_attr); | |
| return ret; | |
| } | |
| pthread_attr_getstacksize(&thread_attr, &default_size); | |
| printf("%s: pthread_attr_getstacksize(%d)!\n", __func__, default_size); | |
| //pthread_detach(rec->thread); | |
| /* | |
| memset(¶m, 0, sizeof(param)); | |
| //param.sched_priority =36; | |
| param.sched_priority = priority; | |
| pthread_setschedparam(thread_t, policy, ¶m); | |
| */ | |
| pthread_attr_destroy(&thread_attr); | |
| printf("%s: end\n", __func__); | |
| return 0; | |
| } | |
| //Start stream | |
| int vbuffer_stream_start(void) | |
| { | |
| int ret = 0; | |
| //voice_buffer_open | |
| int fs = 0; | |
| int buf_size = 0; | |
| printf("Starting vb stream\n"); | |
| tx_optcount = 0; | |
| rx_optcount = 0; | |
| int* buf_int; | |
| int i = 0; | |
| vb_rec.fd = voice_buffer_open(); | |
| if(vb_rec.fd < 0){ | |
| printf("%s: vb open fail, fd=%d\n", __func__, vb_rec.fd); | |
| ret = -1; | |
| goto err; | |
| } | |
| ret = ioctl(vb_rec.fd, VOICE_IOCTL_GET_FS, &fs); | |
| if(ret){ | |
| printf("%s: VOICE_IOCTL_GET_FS fd=%d,ret=%d.\n", __func__, vb_rec.fd, ret); | |
| goto err; | |
| } | |
| if(8000 == fs) | |
| buf_size = 320; | |
| else if(16000 == fs) | |
| buf_size = 640; | |
| else{ | |
| buf_size = 0; | |
| printf("%s: fs is error, buf_size=%d\n", __func__, buf_size); | |
| goto err; | |
| } | |
| #ifdef _TX_RX_DATA_FROM_FILE | |
| printf("%s: open tx and rx file\n", __func__); | |
| if(8000 == fs) | |
| vb_rec.tx_filename = "/mnt/userdata/tx8.pcm"; | |
| else if(16000 == fs) | |
| vb_rec.tx_filename = "/mnt/userdata/tx16.pcm"; | |
| vb_rec.tx_file = fopen(vb_rec.tx_filename, "rb"); | |
| if(!vb_rec.tx_file){ | |
| printf("Unable to open file '%s'\n", vb_rec.tx_filename); | |
| //return 1; | |
| } | |
| if(8000 == fs) | |
| vb_rec.rx_filename = "/mnt/userdata/rx8.pcm"; | |
| else if(16000 == fs) | |
| vb_rec.rx_filename = "/mnt/userdata/rx16.pcm"; | |
| vb_rec.rx_file = fopen(vb_rec.rx_filename, "wb"); | |
| if(!vb_rec.rx_file){ | |
| printf(stderr, "Unable to create file '%s'\n", vb_rec.rx_filename); | |
| //return 1; | |
| } | |
| vb_rec.rx_filesize = 0x10000; | |
| //vb_rec.buf_size = buf_size; | |
| #endif | |
| vb_rec.rx_buf = (char*)malloc(buf_size); | |
| if(!vb_rec.rx_buf){ | |
| printf("%s: malloc rx_buf fail\n", __func__); | |
| goto err; | |
| } | |
| vb_rec.tx_buf = (char*)malloc(buf_size); | |
| if(!vb_rec.tx_buf){ | |
| //free(vb_rec.rx_buf); | |
| printf("%s: malloc tx_buf fail\n", __func__); | |
| goto err; | |
| } | |
| vb_rec.buf_size = buf_size; | |
| #ifdef VB_DATA_LOSS_TEST//for test | |
| buf_int = (int*)vb_rec.tx_buf; | |
| for(i = 0; i < (buf_size / 4); i++){ | |
| *(buf_int + i) = i; | |
| //buf_int[i] = i; | |
| if(i > 0x1f) | |
| printf("cap user: *(buf_int+%d)=%d\n", i, *(buf_int+i)); | |
| } | |
| #endif | |
| vb_rec.quit = 0; | |
| printf("%s: rx tx vb_thread_create start\n", __func__); | |
| ret = vb_thread_create("vb_playback", &vb_rec.rx_thread, vb_rx_thread_func, | |
| 4*1024, 36, NULL); | |
| if(ret != 0){ | |
| printf("%s: rx vb_thread_create fail ret=%d\n", __func__, ret); | |
| goto err; | |
| } | |
| printf("%s: rx vb_thread_create end\n", __func__); | |
| ret = vb_thread_create("vb_record", &vb_rec.tx_thread, vb_tx_thread_func, | |
| 4*1024, 36, NULL); | |
| if(ret != 0){ | |
| printf("%s: tx vb_thread_create fail ret=%d\n", __func__, ret); | |
| vb_rec.quit = 1; | |
| pthread_join(vb_rec.rx_thread,NULL); | |
| vb_rec.rx_thread = NULL; | |
| goto err; | |
| } | |
| printf("%s: tx vb_thread_create end\n", __func__); | |
| //vb_rec.quit = 0; | |
| return 0; | |
| err: | |
| if(vb_rec.rx_buf != NULL) | |
| free(vb_rec.rx_buf); | |
| if(vb_rec.tx_buf != NULL) | |
| free(vb_rec.tx_buf); | |
| if(vb_rec.fd >= 0) | |
| voice_buffer_close(vb_rec.fd); | |
| return ret; | |
| } | |
| //Stop stream | |
| int vbuffer_stream_stop(void) | |
| { | |
| int ret = 0; | |
| printf("%s: rx tx thread exit start\n", __func__); | |
| vb_rec.quit = 1; | |
| sem_post(&vb_rec.read_sem); | |
| sem_post(&vb_rec.write_sem); | |
| if(vb_rec.tx_thread){ | |
| pthread_join (vb_rec.tx_thread,NULL); | |
| vb_rec.tx_thread = NULL; | |
| printf("tx_thread exit end\n"); | |
| } | |
| if(vb_rec.rx_thread){ | |
| pthread_join (vb_rec.rx_thread,NULL); | |
| vb_rec.rx_thread = NULL; | |
| printf("rx_thread exit end\n"); | |
| } | |
| printf("%s: voice_buffer_close start\n", __func__); | |
| ret = voice_buffer_close(vb_rec.fd); | |
| if(ret != 0){ | |
| printf("%s: vb close fail\n", __func__); | |
| return -1; | |
| } | |
| #ifdef _TX_RX_DATA_FROM_FILE | |
| if(vb_rec.tx_file != NULL){ | |
| fclose(vb_rec.tx_file); | |
| printf("%s: vb close, close tx file\n", __func__); | |
| } | |
| if(vb_rec.rx_file != NULL){ | |
| fclose(vb_rec.rx_file); | |
| printf("%s: vb close, close rx file\n", __func__); | |
| } | |
| #endif | |
| printf("Stopping vb stream end\n"); | |
| return 0; | |
| } | |