环形缓冲区的概念
了解完环形缓冲区的概念后,本文介绍其在音视频项目中的应用。
代码实例
相关函数如下
static int LUX_VENC_SaveStream(int chnId, IMPEncoderStream *stream)
{
int ret = 0;
int i = 0;
int nr_pack = stream->packCount;
IMPEncoderPack *pack = NULL;
uint32_t remSize = 0;
char saveName[32] = {0};
char *formatTable[3] = {"h264", "h264", "jpg"};
static FILE *fp[VIDEO_ENC_CHN_MAX] = {NULL};
if (!fp[chnId])
{
sprintf(saveName, "video_stream_chn%d.%s", chnId, formatTable[chnId]);
fp[chnId] = fopen(saveName, "wb");
if (NULL == fp[chnId])
return 0;
}
for (i = 0; i < nr_pack; i++)
{
pack = &stream->pack[i];
if (pack->length)
{
remSize = stream->streamSize - pack->offset;
if (remSize < pack->length)
{
ret = fwrite((void *)(stream->virAddr + pack->offset), 1, remSize, fp[chnId]);
if (ret != remSize)
{
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].remSize(%d) len:%d\n", ret, i, remSize, ret);
return -1;
}
ret = fwrite((void *)stream->virAddr, 1, pack->length - remSize, fp[chnId]);
if (ret != (pack->length - remSize))
{
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].(length-remSize)(%d) len:%d\n", ret, i, (pack->length - remSize), ret);
return -1;
}
}
else
{
ret = fwrite((void *)(stream->virAddr + pack->offset), 1, pack->length, fp[chnId]);
if (ret != pack->length)
{
IMP_LOG_ERR(TAG, "stream write ret(%d) != pack[%d].length(%d) len:%d\n", ret, i, pack->length, ret);
return -1;
}
}
}
}
return 0;
}
所用到的数据结构如下
/**
* 定义编码帧码流包结构体
*/
typedef struct {
uint32_t offset; /**< 码流包地址偏移 */
uint32_t length; /**< 码流包长度 */
int64_t timestamp; /**< 时间戳,单位us */
bool frameEnd; /**< 帧结束标识 */
IMPEncoderNalType nalType; /**< H.264和H.265编码Channel码流NAL类型 */
IMPEncoderSliceType sliceType;
} IMPEncoderPack;
/**
* 定义编码帧码流类型结构体
*/
typedef struct {
uint32_t phyAddr; /**< 帧码流物理地址 */
uint32_t virAddr; /**< 帧码流包虚拟地址 */
uint32_t streamSize; /**< virAddr对应分配的地址空间大小 */
IMPEncoderPack *pack; /**< 帧码流包结构 */
uint32_t packCount; /**< 一帧码流的所有包的个数 */
uint32_t seq; /**< 编码帧码流序列号 */
} IMPEncoderStream;
代码解释
1、获取包的总数作为循环条件
2、将当前包的属性存入IMPEncoderPack
结构体中
3、第一层判断,是为了区分是否还有包要传。若没有包,则对应结构体中的所有数据都是0
4、判断是否需要折返
相关变量描述
- virAddr——虚拟地址(内存首地址)
- streamSize——虚拟地址的长度
- pack->offset——基于virAddr的偏移量,用于计算包的位置
- pack->length——包的长度
- remSize——streamSize中剩余可用的存储空间
remsize忘记画了,remSize = StreamSize - offset
即除了offset外的可用于存储的空间
上图的length写错了,应该是所有包的长度
折返判断
if (remSize < pack->length)
{
ret = fwrite((void *)(stream->virAddr + pack->offset), 1, remSize, fp[chnId]);
if (ret != remSize)
return -1;
ret = fwrite((void *)stream->virAddr, 1, pack->length - remSize, fp[chnId]);
if (ret != (pack->length - remSize))
return -1;
}
else
{
ret = fwrite((void *)(stream->virAddr + pack->offset), 1, pack->length, fp[chnId]);
if (ret != pack->length)
return -1;
}
去掉打印后如上
判断条件为remSize < pack->length
,即剩余空间是否能够存放剩下的包
else中,剩余空间能够放下所有的包
于是直接从首地址加上偏移量的位置开始存
if中,剩余空间不能存完全部的包长
首先从头开始存,存不下之后,剩余的从首地址开始存
以此达到环形buffer的效果