/***************************************************************************** | |
** °æÈ¨ËùÓÐ (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; | |
} | |