RingBuffer的实际使用场景很多.下面分别写了阻塞和非阻塞两种模式的代码。
非阻塞模式可以自己替换ffmpeg里面的fifo(同一线程先写后读),如果我们不想使用fifo,就可以直接使用下面的代码。
如果是用SDL播放audio,存和读在不同线程,那么就需要阻塞方式。(下面实现的CGNonBlockRingBuffer写是阻塞的,读是非阻塞的,因为很多场景下我们更加需要的是读不要阻塞)
上述的两种场景已通过下面代码实际测试通过。
代码在mac os上运行。windows平台把sys/malloc.h改成malloc.h ,unistd.h改成Windows.h,代码中usleep(1000)改成Sleep(1)
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define debug(fmt,...) printf("" __FILE__" %s:%d $ " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
class CGBlockRingBuffer
{
/**
start:下一次数据读的pos
end: 下一次数据写的pos
end-start=available data 可读取的数据区
length-1-end:available space 可填充的数据区
写是阻塞模式,读是非阻塞模式
*/
private:
uint8_t *buffer;
int length;
int start;
int end;
pthread_mutex_t mutex;
pthread_cond_t cond; //当队列现在不满了,生产者需要等待消费者发送此信号
int abort_request;
public:
CGBlockRingBuffer()
{
}
~CGBlockRingBuffer()
{
if (buffer) {
free(buffer);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
void Init(int size)
{
this->length = size + 1;
start = 0;
end = 0;
abort_request = 0;
buffer = (uint8_t*)malloc(this->length);
memset(buffer, 0, this->length);
pthread_mutex_init(&mutex, nullptr);
pthread_cond_init(&cond, nullptr);
}
int Write(uint8_t *data, int size)
{
int ret = 0;
if (size>this->length - 1){
printf("size too large ! can not write\n");
return -1;
}
if (abort_request){
return -1;
}
pthread_mutex_lock(&mutex);
if (end - start == 0) {
start = end = 0;
}
while (size>this->length - end - 1){
//printf("write no enough space: %d request, %d available\n", size, this->length - end - 1);
pthread_cond_wait(&cond, &mutex);
if (abort_request){
ret = -1;
break;
}
}
if (!abort_request){
if (size>0 && size <= this->length - end - 1){
memcpy(buffer + end, data, size);
end += size;
}
}
pthread_mutex_unlock(&mutex);
return ret;
}
int Read(uint8_t *target, int size)
{
int ret = 0;
if (size>this->length - 1){
printf("size too large ! can not read\n");
return -1;
}
if (abort_request){
return -1;
}
pthread_mutex_lock(&mutex);
int available_data = end - start;
int wsize = size > available_data ? available_data : size;
memcpy(target, buffer + start, wsize);
start += wsize;
ret = wsize;
if (end - start == 0) {
start = end = 0;
}
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return ret;
}
void req_abort()
{
pthread_mutex_lock(&mutex);
abort_request = 1;
start = end = 0;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
void req_activate(){
pthread_mutex_lock(&mutex);
abort_request = 0;
start = end = 0;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
};
class CGNonBlockRingBuffer
{
/**
start:下一次数据读的pos
end: 下一次数据写的pos
end-start=available data 可读取的数据区
length-1-end:available space 可填充的数据区
非阻塞模式,初始size最好设置大一点,比如max(read size,write size)的3倍
*/
private:
int length;
int start;
int end;
uint8_t *buffer;
int full;
int empty;
public:
CGNonBlockRingBuffer()
{
}
~CGNonBlockRingBuffer()
{
if (buffer) {
free(buffer);
}
}
void Init(int size)
{
this->length = size + 1;
start = 0;
end = 0;
full=0;
empty=1;
buffer = (uint8_t*)malloc(this->length);
memset(buffer,0,this->length);
}
int Write(uint8_t *data, int size)
{
int ret=0;
if(size>this->length-1){
printf("size too large ! can not write\n");
return -1;
}
if(full){
printf("is full\n");
return -1;
}else if(end>=start){
int available_space= this->length - end - 1;
if(size>available_space){
int writesize=available_space;
memcpy(buffer+end, data, writesize);
if(size-writesize>start){
printf("error called,this length too small left%d start%d\n",size-writesize,start);
return -1;
}else{
memcpy(buffer, data+writesize, size-writesize);
end =(size-writesize);
if(end==start){
full=1;
}
}
}else{
memcpy(buffer+end, data, size);
end+=size;
}
}else if(endavailable_space){
printf("error2 called,this length too small \n");
return -1;
}else{
memcpy(buffer+end, data, size);
end+=size;
if(end==start){
full=1;
}
}
}
if(ret==0){empty=0;}
return ret;
}
int Read(uint8_t *target, int size)
{
int ret=0;
if(size>this->length-1){
printf("size too large ! can not read\n");
return -1;
}
if(empty){
printf("is empty\n");
return -1;
}else if(end>=start){
int available_data=end-start;
if(size>available_data){
printf("1read no enough data : has %d, needs %d\n",available_data,size);
return -1;
}else{
memcpy(target,buffer+start,size);
start+=size;
if(start==end){
empty=1;
start=end=0;
}
}
}else if(endavailable_data){
printf("2read no enough data : has %d, needs %d\n",available_data,size);
return -1;
}else{
if(size>=length-1-start){
memcpy(target,buffer+start,length-1-start);
memcpy(target+length-1-start,buffer,size-(length-1-start));
start=size-(length-1-start);
}else{
memcpy(target,buffer+start,size);
start+=size;
}
if(start==end){
empty=1;
start=end=0;
}
}
}
if(ret==0){full=0;}
return ret;
}
};
void* func1(void* arg){
CGBlockRingBuffer* blockRingBuffer=(CGBlockRingBuffer*)arg;
char* a="abcdefghijklmn";
for(int i=0;i<10;i++){
if(i==6){
blockRingBuffer->req_abort();
}
int ret=blockRingBuffer->Write((uint8_t*)a,10);
if(ret<0)break;
sleep(1);
}
printf("func1 end called\n");
return NULL;
}
void* func2(void* arg){
CGBlockRingBuffer* blockRingBuffer=(CGBlockRingBuffer*)arg;
uint8_t* dst=(uint8_t*)malloc(16);
for(int i=0;i<10;i++){
memset(dst,0,16);
int count=12;
int rsize=0;
while(count>0){
int ret=blockRingBuffer->Read(dst+rsize,count);
if(ret<0)break;
count-=ret;
rsize+=ret;
}
cout<<"dst:"<Init(16);
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,func1,blockRingBuffer);
pthread_create(&tid1,NULL,func2,blockRingBuffer);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
delete blockRingBuffer;
//以下是非阻塞模式ringbuf测试
CGNonBlockRingBuffer* ringBuffer=new CGNonBlockRingBuffer();
ringBuffer->Init(30);
char* a="abcdefghijklmn";
uint8_t* dst=(uint8_t*)malloc(16);
for(int i=0;i<10;i++){
int ret=ringBuffer->Write((uint8_t*)a,10);
if(ret<0)break;
memset(dst,0,16);
ret=ringBuffer->Read(dst,12);
if(ret==0)
cout<<"dst:"<