实现一个阻塞与非阻塞两种方式的环形缓冲区RingBuffer

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:"<

 

你可能感兴趣的:(ffmpeg)