视频监控安防平台-自定义文件格式MFS存储
首先在视频监控领域存储是最重要的,为什么怎么说呢,都是监控是用来事后处理事物的,所以说存储是多么重要的,要想有一个比较稳定的存储,必须要好好规划,我尝试过自定义avi格式存储和mp4格式存储,如果用在简单的项目上还好没什么问题,但是如果在大型项目上面(比如1000路高清摄像头的存储),那么问题就会慢慢暴露出来,最常见的问题就是写入磁盘的时候会卡个几秒几分钟,在项目上可想而知这就意味着丢几秒几分钟视频,后果是相对严重的,由于遇到这样的问题iostat、iotop这些监控磁盘信息的数据都显示压力大写不进去,这也是给我了重新设计自己的存储格式,首先存储格式必须简单而且易用,自己设计还有好处就是可以任意实现自己的想要的功能,比如单帧播放、单帧倒放、倒放、同步回放等很多项目都必须要的功能。下面简单说说自己的设计思路,首先自己构思存储的结构图:
存储文件格式思想大概是预留1M来存储每个I帧P帧等信息,最后预留1M来预防最后一个I帧无法全部写入,详细看结构图,应该比较易懂。
设计结构搞完了,就开始正式写代码了,下面粘贴一下代码:
公共头文件mfs.h
#ifndef LIB_MFS_H__
#define LIB_MFS_H__
#include <vector>
#include "Log.h"
#define MFS8K (8<<10)//8192
#define MFS16K (16<<10)//16384
#define MFS24K (24<<10)//24576
#define MFS32K (32<<10)//32768
#define MFS64K (64<<10)//65536
#define MFS128K (128<<10)//131072
#define MFS256K (256<<10)//262144
#define MFS257K (257<<10)//263168
#define MFS512K (512<<10)//524288
#define MFS820K (820<<10)//524288
#define MFS1M (1<<20)
#define MFS2M (2<<20)
#define MFS16M (16<<20)//16777216
#define MFS32M (32<<20)//33554432
#define MFS64M (64<<20)//33554432
#define MFS128M (128<<20)//134217728
#define FILESIZE_HEADINFO (4<<10)//4096
#define FILESIZE_HEAD (1<<20)
#define FILESIZE_RESERVE (1<<20)
#define DEFAULT_DATA_CACHE MFS2M //默认数据缓存大小
#define DEFAULT_FILE_SIZE MFS32M //默认文件大小
#define MFS_HEAD_VERSION "MFS VERSION=1.0" //文件头固定格式
#define MFS_START_CODE "MFS" //I帧索引,数据索引固定头
//编码格式
typedef enum
{
ENCODER_TYPE_NONE = 0,
ENCODER_TYPE_MPEG2 = 1,
ENCODER_TYPE_MPEG4 = 2,
ENCODER_TYPE_H264 = 3,
ENCODER_TYPE_H265 = 4
}ENCODER_TYPE_E;
//数据类型
typedef enum
{
DATA_TYPE_NONE = 0,
DATA_TYPE_I = 1,
DATA_TYPE_P = 2,
DATA_TYPE_B = 3,
DATA_TYPE_A = 4,
DATA_TYPE_EVENT = 5,
DATA_TYPE_SUNTILE = 6
}DATA_TYPE_E;
//文件头结构体
struct MFS_FILE_HEAD{
char version[24]; //用于文件合法性检测 版本号, "MFS VERSION=1.0"
unsigned int flag; //文件的标示符 0:空闲; 1:使用中; 3:不可用
unsigned int time_start; //文件的开始时间
unsigned int time_end; //文件的结束时间
unsigned int index_size; //文件头长度,从4k头之后,开始算长度
unsigned int idx_amount; //包含的I帧索引项总数
unsigned int data_size; //数据区总厂
unsigned int framerate; //帧率
unsigned char sps_pps[256]; //文件sps_pps的内容
unsigned int sps_pps_len; //文件sps_pps的长度
unsigned int manuf; //厂家类型,用于私有码流
} __attribute__((packed, aligned(1)));
//I帧索引 //固定大小 数组, 20字节
struct MFS_I_FRAME_INDEX{
unsigned char segment_normal[3]; //固定标志 "MFS"
unsigned char encoder_type; //I帧的编码格式 1:MEPG-2; 2:MEPG-4; 3:H264 ;4:H265
unsigned int idx_offset; //I帧在文件中的偏移量, 从数据区开始算
unsigned int i_data_size; //I帧长度,不包含数据头
long long time_stamp; //I帧的时间戳 毫秒
} __attribute__((packed, aligned(1)));
//数据索引,数据类型包括:I帧、P帧、B帧、事件信息,字符等, 16字节
struct MFS_DATA_INDEX{
unsigned char segment_normal[3]; //固定标志 "MFS"
unsigned char data_type; //数据类型 1:I帧 ;2:P帧; 3:B帧; 4:音频; 5:事件帧; 6:字幕帧
unsigned int data_size; //数据长度,从MFS_DATA_INDEX 头之后开始算
long long time_stamp; //数据的时间戳 毫秒
} __attribute__((packed, aligned(1)));
//-----------------------------------------------------------------------------------------------------------------
#endif
应用实例demo:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "mfs_write.h"
#include "mfs_read.h"
unsigned char dig[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9','a', 'b', 'c', 'd', 'e', 'f'};
//dd if=/dev/zero of=testfile1.mfs bs=32M count=1
unsigned char testbuffer_I[32] = {"123456789012345678901234567890\0"};
unsigned char testbuffer_D[16] = {"ABCDEFGHIJKLMN\0"};
unsigned char testbuffer_P[8] = {"!@#$%^\0"};
int main(int argc, char *argv[])
{
char filename[128] = {"testfile1.mfs"};
if (argc >= 2)
{
sprintf(filename, "%s", argv[1]);
}
printf("filename is %s\n\n", filename);
memset(testbuffer_I, 'I', sizeof(testbuffer_I));
memset(testbuffer_D, 'D', sizeof(testbuffer_D));
memset(testbuffer_P, 'P', sizeof(testbuffer_P));
#if 0
CMfsWrite mfs_write;
mfs_write.mfs_init();
mfs_write.mfs_open(filename);
int times = 0;
while(times++ < 10)
{
printf("\nwritefile times:%d\n", times);
testbuffer_I[0] = (unsigned char)dig[times];
testbuffer_D[0] = (unsigned char)dig[times];
testbuffer_P[0] = (unsigned char)dig[times];
mfs_write.mfs_put(testbuffer_P, sizeof(testbuffer_P), DATA_TYPE_P);
mfs_write.mfs_put(testbuffer_P, sizeof(testbuffer_P), DATA_TYPE_P);
mfs_write.mfs_put(testbuffer_I, sizeof(testbuffer_I), DATA_TYPE_I);
usleep(100*1000);
mfs_write.mfs_put(testbuffer_D, sizeof(testbuffer_D), DATA_TYPE_EVENT);
usleep(100*1000);
for (int i = 0; i<3; i++)
{
mfs_write.mfs_put(testbuffer_P, sizeof(testbuffer_P), DATA_TYPE_P);
usleep(100*1000);
}
if (times%3==0)
mfs_write.mfs_write(true);
//usleep(10*1000);
}
mfs_write.mfs_write(true);
mfs_write.mfs_close();
printf("\n\n-----------------------------------------------------------------------------------------------------\n\n");
#endif
//
CMfsRead mfs_read;
mfs_read.mfs_init();
mfs_read.mfs_open(filename);
struct MFS_READ readdata;
int nRet = 0;
mfs_read.head_print_test(true);
//mfs_read.mfs_location(2);
MFS_READ_TYPE_E readtype = MFS_READ_TYPE_GOP;
bool bBack = false;
FILE *fp = NULL;
if (argv[2] != NULL)
{
char writefilename[64] = {0};
sprintf(writefilename, "./%s", argv[2]);
fp = fopen(writefilename, "w+");
printf("open write filename :%s result:%s!\n\n", writefilename, (fp == NULL)?"fail":"sucess");
}
#if 1
while(1)
{
nRet = mfs_read.mfs_read(readtype, readdata, bBack);
if (nRet < 0)
{
break;
}
int plen = 0;
printf("\nmfs_read_frame eDecoderType:%d, vmfsbuf.size:%d\n", (int)readdata.eDecoderType, (int)readdata.vmfsbuf.size());
if (readdata.vmfsbuf.size() > 0)
{
printf("Data eDataType(%d) bKeyFrame(%d) nPts(%lld) len:%d\n", (int)readdata.vmfsbuf[0].eDataType, readdata.vmfsbuf[0].bKeyFrame, readdata.vmfsbuf[0].nPts, readdata.vmfsbuf[0].nLen);
plen = readdata.vmfsbuf[0].nLen > 10 ? 10 : readdata.vmfsbuf[0].nLen;
for (int i = 0; i< plen; ++i )
{
printf("0x%c ", readdata.vmfsbuf[0].pData[i]);
}
printf("\n\n");
if (fp != NULL)
{
for (unsigned int j=0; j<readdata.vmfsbuf.size(); j++)
{
fwrite(readdata.vmfsbuf[j].pData, sizeof(char), readdata.vmfsbuf[j].nLen, fp);
}
}
}
readdata.vmfsbuf.clear();
usleep(10*1000);
}
if (fp != NULL)
{
fclose(fp);
fp = NULL;
}
#else
nRet = mfs_read.mfs_read_time(1, readdata);
printf("mfs_read_time Data Sn(%d) PayLoadType(%d) Manuf(%d) eDecoderType:%d\n", readdata.Sn, readdata.PayLoadType, readdata.Manuf, (int)readdata.eDecoderType);
int plen = 0;
for (unsigned int k = 0; k < readdata.vmfsbuf.size(); ++k)
{
printf("Data eDataType(%d) bKeyFrame(%d) nPts(%lld) len:%d\n", (int)readdata.vmfsbuf[k].eDataType, readdata.vmfsbuf[k].bKeyFrame, readdata.vmfsbuf[k].nPts, readdata.vmfsbuf[k].nLen);
plen = readdata.vmfsbuf[k].nLen > 10 ? 10 : readdata.vmfsbuf[k].nLen;
for (int i = 0; i< plen; ++i )
printf("0x%02x ", readdata.vmfsbuf[k].pData[i]);
printf("\n");
}
#endif
mfs_read.mfs_close();
return 0;
}
目前项目上在大量使用这样的存储格式文件,还比较稳定。