RingBuffer的實際使用場景很多.下面分別寫了阻塞和非阻塞兩種模式的代碼。
非阻塞模式可以自己替換ffmpeg裏面的fifo(同一線程先寫後讀),如果我們不想使用fifo,就可以直接使用下面的代碼。
如果是用SDL播放audio,存和讀在不同線程,那麼就需要阻塞方式。(下面實現的CGNonBlockRingBuffer寫是阻塞的,讀是非阻塞的,因爲很多場景下我們更加需要的是讀不要阻塞)
上述的兩種場景已通過下面代碼實際測試通過。
代碼在mac os上運行。windows平臺把sys/malloc.h改成malloc.h ,unistd.h改成Windows.h,代碼中usleep(1000)改成Sleep(1)
#include <stdio.h>
#include <iostream>
#include<sys/malloc.h>
#include <memory>
#include <pthread.h>
#include<unistd.h>
#include<string>
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(end<start){
int available_space= start - end;
if(size>available_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(end<start){
int available_data=length-1-start+end;
if(size>available_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:"<<dst<<endl;
sleep(2);
}
printf("func2 end called\n");
return NULL;
}
int main(int argc, char *argv[])
{ //以下是阻塞模式ringbuf測試
CGBlockRingBuffer* blockRingBuffer=new CGBlockRingBuffer();
blockRingBuffer->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:"<<dst<<endl;
}
delete ringBuffer;
free(dst);
debug("main end called\n");
return 0;
}