在开发过程中经常会遇到需要使用环形缓冲的地方,比如在流媒体方面,对于接收到的音视频数据的存储、以及音频解码后PCM数据的存储等问题上,最好使用一个环形缓冲,接收到,或者解码后压入该缓冲区中,在需要解码,或者需要塞入声卡时,再从该缓冲区中读取,这样压入和取出同一个缓冲区,既方便快捷,又安全有效。源码如下:
typedef char s8;typedef unsigned char u8;typedef short s16;typedef unsigned short u16;typedef int s32;typedef unsigned int u32;typedef unsigned uint32_t;typedef void *FIFOBUFFERHANDLE; //定义一个指针,方便外部引用#ifndef _max#define _max(a, b) a > b ? a : b#endif#ifndef _min#define _min(a, b) a > b ? b : a#endif//定义一个结构体,用于记录该缓冲区的开始、读取、写入、接收的内存地址typedef struct _TFIFOBUFFER{ u8 *pu8Buffer, *pu8Read, *pu8Write, *pu8End;}TFIFOBUFFER, *PTFIFOBUFFER;//创建一个指定大小的缓冲区FIFOBUFFERHANDLE FifoBufferCreate(s32 s32Size){ PTFIFOBUFFER ptFifoBuffer = NULL; do { ptFifoBuffer = (PTFIFOBUFFER)malloc(sizeof(TFIFOBUFFER)); if(NULL == ptFifoBuffer) { break; } memset(ptFifoBuffer, 0, sizeof(TFIFOBUFFER)); ptFifoBuffer->pu8Buffer = (u8 *)malloc(s32Size); if(NULL == ptFifoBuffer->pu8Buffer) { break; } memset(ptFifoBuffer->pu8Buffer, 0, s32Size); ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer; ptFifoBuffer->pu8End = ptFifoBuffer->pu8Buffer + s32Size; if(NULL == ptFifoBuffer->pu8Buffer) { break; } return (FIFOBUFFERHANDLE)ptFifoBuffer; }while(false); if(NULL != ptFifoBuffer) { if(NULL != ptFifoBuffer->pu8Buffer) { free(ptFifoBuffer->pu8Buffer); ptFifoBuffer->pu8Buffer = NULL; } free(ptFifoBuffer); ptFifoBuffer = NULL; } return NULL;}//释放一个缓冲区void FifoBufferDestroy(FIFOBUFFERHANDLE pHandle){ PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL != ptFifoBuffer) { if(NULL != ptFifoBuffer->pu8Buffer) { free(ptFifoBuffer->pu8Buffer); ptFifoBuffer->pu8Buffer = NULL; } free(ptFifoBuffer); ptFifoBuffer = NULL; }}//重置缓冲区中读写指针void FifoBufferReset(FIFOBUFFERHANDLE pHandle){ PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL != ptFifoBuffer) { ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer; }}//获取大小int FifoBufferSize(FIFOBUFFERHANDLE pHandle){ s32 s32Size = 0; PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL != ptFifoBuffer) { s32Size = ptFifoBuffer->pu8Write - ptFifoBuffer->pu8Read; if(s32Size < 0) { s32Size += ptFifoBuffer->pu8End - ptFifoBuffer->pu8Buffer; } } return s32Size;}//向缓冲区中写入数据s32 FifoBufferWrite(FIFOBUFFERHANDLE pHandle, u8 *pu8Buffer, s32 s32Size){ s32 s32Length = 0; PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL == ptFifoBuffer) { return 0; } do { s32Length = _min(ptFifoBuffer->pu8End - ptFifoBuffer->pu8Write, s32Size); memcpy(ptFifoBuffer->pu8Write, pu8Buffer, s32Length); pu8Buffer = pu8Buffer + s32Length; ptFifoBuffer->pu8Write += s32Length; //向后偏移写指针 //如果缓冲区写指针到达了缓冲区尾部,则将写指针移动到缓冲区开始地址,实现真正的环 //形缓冲 if(ptFifoBuffer->pu8Write >= ptFifoBuffer->pu8End) { ptFifoBuffer->pu8Write = ptFifoBuffer->pu8Buffer; } s32Size -= s32Length; }while(s32Size > 0); return 1;}//读数据s32 FifoBufferRead(FIFOBUFFERHANDLE pHandle, u8 *pu8Buffer, s32 *ps32Size){ s32 s32Length = 0, pTempSize = (*ps32Size); PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL == ptFifoBuffer || NULL == pu8Buffer || 0 > pTempSize) { return 0; } if(0 == pTempSize) { (*ps32Size) = 0; return 1; } (*ps32Size) = 0; do { s32Length = _min(FifoBufferSize(pHandle), pTempSize); s32Length = _min(ptFifoBuffer->pu8End - ptFifoBuffer->pu8Read, s32Length); if(0 == s32Length) { break; } memcpy(pu8Buffer, ptFifoBuffer->pu8Read, s32Length); pu8Buffer = pu8Buffer + s32Length; ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Read + s32Length;//向后偏移读指针 //如果缓冲区读指针到达了缓冲区尾部,则将读指针移动到缓冲区开始地址,实现真正的环 //形缓冲 if(ptFifoBuffer->pu8Read >= ptFifoBuffer->pu8End) { ptFifoBuffer->pu8Read = ptFifoBuffer->pu8Buffer; } pTempSize -= s32Length; (*ps32Size) += s32Length; }while(pTempSize > 0); return 1;}s32 FifoBufferShade(FIFOBUFFERHANDLE pHandle, s32 s32Offset){ PTFIFOBUFFER ptFifoBuffer = (PTFIFOBUFFER)pHandle; if(NULL != ptFifoBuffer) { if((ptFifoBuffer->pu8Read + s32Offset) > ptFifoBuffer->pu8End) { return *(ptFifoBuffer->pu8Buffer + (s32Offset - (ptFifoBuffer->pu8End - ptFifoBuffer->pu8Read))); } else { return *(ptFifoBuffer->pu8Read + s32Offset); } } return 0;}
使用该缓冲区时,初始化一块大小合适的内存,并在外部进行加锁,同时需要读写操作保持基本一致,即可;如果用户设置的缓冲区过小,或者读写速率差距较大,则会造成用户数据的丢失。
以上代码使用纯C写的,当然也可以使用C++进行编写,写一个类,借助string类能开发出一套更简洁的缓冲缓冲代码,只要注意逻辑实现不出问题即可。