当前位置:网站首页>【富瀚6630编码存录像,用rtsp服务器及时间戳同步实现vlc观看录像】
【富瀚6630编码存录像,用rtsp服务器及时间戳同步实现vlc观看录像】
2022-07-03 02:42:00 【I&You】
富瀚6630编码存录像,用rtsp服务器及时间戳同步实现vlc观看录像
代码逻辑主要讲几个重要的函数
编码线程_thread_venc_data(按帧保存到录像文件中)
if(stStream.u32PackCount > 0)
{
s32Ret = FY_MPI_VENC_GetStream(i, &stStream, FY_TRUE);//获取一帧
}
if (FY_SUCCESS != s32Ret)
{
continue;
}
else
{
#if 1
if(i==4){
int len=0,off=0;
memset(&stVideoData, 0, sizeof(stVideoData));
stVideoData.buffer = malloc(200*1024);
//分拆包,获取有效流数据
for (k = 0; k < stStream.u32PackCount; k++)
{
len+=stStream.pstPack[k].u32Len-stStream.pstPack[k].u32Offset;
}
for (k = 0; k < stStream.u32PackCount; k++)
{
memcpy(stVideoData.buffer+off,stStream.pstPack[k].pu8Addr+stStream.pstPack[k].u32Offset,stStream.pstPack[k].u32Len-stStream.pstPack[k].u32Offset);
off+=stStream.pstPack[k].u32Len-stStream.pstPack[k].u32Offset;
}
stVideoData.len =len;
stVideoData.frametype = LXR_FRAME_TYPE_UNKNOWN; //无用
stVideoData.timestamp = stStream.pstPack[0].u64PTS*1000; //无用,上层重新获取赋值
if(i < s32ChnTotal/2)
streamID = 0; //main stream
else
streamID = 1; //sub stream
conn = i%(s32ChnTotal/2); //conn,通道号
_dvr_handle_video_frame(conn,streamID,&stVideoData, LXR_VIDEO_TYPE_H264);
}
_dvr_handle_video_frame(按帧保存到录像文件中)
void _dvr_handle_video_frame(int ch, int streamid, LXRFrame_t* m_frame, LXR_VIDEO_TYPE_E encType)
{
LXRFrame_t frame;
memcpy(&frame, m_frame, sizeof(LXRFrame_t));
//解析宽高
//JBOOL bIFrame = JFALSE; //是否I帧
FY_BOOL bNeedCmpareWH = FY_FALSE; //需要比较宽高
int frameW = 0, frameH = 0;
LXR_VIDEO_TYPE_E vencType = LXR_VIDEO_TYPE_H264;
unsigned long long timeStap =arch_getSystemStartupMs(); //必须加时间戳,否则不能回放
frame.timestamp = timeStap;
if(encType == LXR_VIDEO_TYPE_H264)
{
vencType = LXR_VIDEO_TYPE_H264;
unsigned int NaluType = frame.buffer[4]&0x1f;
if((NaluType == 7) || (NaluType == 8) || (NaluType == 5))//判断是否为i帧
{
//bIFrame = JTRUE;
frame.frametype = LXR_FRAME_TYPE_I;
}
if(NaluType == 7)
{
//测试执行时间。(3535)
//带__compare_sps: 2us
//不带 : 7us
//struct timeval tmBegin,tmNow;
//gettimeofday(&tmBegin, 0);
//检查sps是否需要解析
//解析sps
#if 0
sps_data_t sps;
h264_parse_sps(frame.buffer+5, frame.len-5, &sps);
frameW = sps.width;
frameH = sps.height;
bNeedCmpareWH = FY_TRUE;
#endif
}
}
static FILE * fp=NULL;
static unsigned char first_iframe = 0;
if(fp==NULL){
fp = fopen("/mnt/h264_stream/lxr_test_record5.h264", "w");
}
if(fp == NULL){
perror("open_recordfile_fail");
}
if(!first_iframe&&frame.frametype==LXR_FRAME_TYPE_I){
first_iframe =1;
}
if(first_iframe){
//第一帧要存关键帧,否则vlc播放不了
unsigned char sendBuf[200*1024];
unsigned char *p = (unsigned char*)sendBuf;
int size1,size2,size3,ret;
StramFrame_t grpc_frame={
0};//保存到录像文件的帧结构体
//赋值
grpc_frame.buffer=frame.buffer;
grpc_frame.ch=ch;
grpc_frame.frametype = frame.frametype;
grpc_frame.len=frame.len;
grpc_frame.stream_id = streamid;
grpc_frame.timestamp = frame.timestamp;
//拷贝数据
size1 = sizeof(StramFrame_t);
size2 = grpc_frame.len;
size3 = size1+size2;
*((unsigned int*)p) = size1 + size2 ;
memcpy(p+sizeof(unsigned int), &grpc_frame, size1);
memcpy(p+sizeof(unsigned int)+size1, grpc_frame.buffer, size2);
if(size3>61440){
//一次写入60k,分多次写入,这个可以自己定义一次写入64k也可
int i=0;
while(size3>61440){
ret =fwrite(p+i,61440,1,fp);
i+=61440;
size3-=61440;
}
ret =fwrite(p+i,size3,1,fp);
}else{
ret =fwrite(p,sizeof(unsigned int)+size1+size2,1,fp);
}
}
free(frame.buffer);
}
read_stream(录像文件保存好后)(vlc用url请求播放开始读录像文件线程)
void read_stream(void *arg){
int fd=0,ret;
unsigned char recBuf_init[200*1024];
unsigned char * recBuf=(unsigned char *)recBuf_init;
if(fd==0){
fd = open("/mnt/h264_stream/lxr_test_record5.h264", O_RDONLY);
}
if(fd <0 ){
perror("open_recordfile_fail");
}
while(1)
{
ret = read(fd,recBuf,61440);
if(ret==0){
//读完走人
return;
}
unsigned int lenTotal = *((unsigned int*)recBuf)+PACKET_HEAD;
if(ret>lenTotal){
//沾包
unsigned int readlen=lenTotal,total=ret;
//写入数据
write_stream(recBuf,lenTotal,fd);
unsigned char * recBuf_offset = recBuf;
while (readlen<ret)//判断拆包是否完毕
{
//偏移到下一包的首地址
recBuf_offset+=lenTotal;
lenTotal = *((unsigned int*)recBuf_offset)+PACKET_HEAD;
//判断是否为I帧分包
if(i_frame_flag(recBuf_offset)){
unsigned int iframe_read=ret-readlen;
while (iframe_read<lenTotal)//说明粘包中不包含完整I帧需要取包
{
ret = read(fd,recBuf_offset+iframe_read,lenTotal-iframe_read);
iframe_read+=ret;
}
write_stream(recBuf_offset,lenTotal,fd);
readlen+=lenTotal;
}else{
//P帧包
if((readlen+lenTotal)>ret){
int tmps=lenTotal-(ret-readlen);//用来存临时变量
ret = read(fd,recBuf_offset+ret-readlen,tmps);
readlen = tmps;
write_stream(recBuf_offset,lenTotal,fd);
}else{
write_stream(recBuf_offset,lenTotal,fd);
readlen+=lenTotal;
}
}
}
}
else if(i_frame_flag(recBuf)){
//判断是否为i帧包
unsigned int iframe_read=ret;
while(iframe_read<lenTotal){
//I帧分包发送
ret = read(fd,recBuf+iframe_read,61440);
iframe_read+=ret;
}
if(lenTotal!=iframe_read){
//出现粘包
unsigned int not_readlen = iframe_read-lenTotal;
unsigned int readlen=0;
unsigned char * recBuf_offset=recBuf;
write_stream(recBuf,lenTotal,fd);//先把完整I帧写入
while (not_readlen!=readlen)
{
//偏移到下一P帧的首地址
recBuf_offset+=lenTotal;
lenTotal = *((unsigned int*)recBuf_offset)+4;
write_stream(recBuf_offset,lenTotal,fd);
readlen+=lenTotal;
}
}else if(lenTotal==iframe_read){
//单包i帧
write_stream(recBuf,lenTotal,fd);
}
}else{
//P帧包
write_stream(recBuf,lenTotal,fd);
}
}
}
write_stream(按时间戳同步把帧数据添加到rtsp队列中)
void write_stream(unsigned char *recBuf,unsigned int lenTotal,int fd){
//把头部去掉
unsigned char *head=(unsigned char *)recBuf+4;
StramFrame_t * recFrm=(StramFrame_t *)head;
if(recFrm)
{
recFrm->buffer = (unsigned char*)recFrm + sizeof(StramFrame_t);
if(lenTotal-4 != recFrm->len + sizeof(StramFrame_t))
{
printf("bad length:chn=%d,lenTotal=%d,frmLen=%d\n", recFrm->ch, lenTotal, recFrm->len);
}
}
//比较时间戳
FY_BOOL bSendPlay = FY_FALSE; //是否播放
static RemoteSync_t syncFrame={
0};
static unsigned char first_comehere=1;
if(first_comehere)
{
first_comehere = 0;
//还没建立同步。肯定要播放
bSendPlay = FY_TRUE;
syncFrame.iFrame = recFrm->frametype;
syncFrame.tsFrame = recFrm->timestamp;
syncFrame.tsPlay = arch_getSystemStartupMs();
//player->tsCurPlay = player->syncFrame.tsPlay;
}
else
{
//已经建立同步
//播放时间差大于 帧时间差 才播放
unsigned long long tsNow;
do
{
tsNow = arch_getSystemStartupMs();
} while ((tsNow - syncFrame.tsPlay)*1 < recFrm->timestamp - syncFrame.tsFrame);
bSendPlay = FY_TRUE;
}
if(n_1<NMAX&&bSendPlay==FY_TRUE)
{
memcpy(ringfifo_1[iput_1].buffer,recFrm->buffer,recFrm->len);
ringfifo_1[iput_1].size= recFrm->len;
if(recFrm->frametype==LXR_FRAME_TYPE_I)
{
ringfifo_1[iput_1].frame_type = FRAME_TYPE_I;
}
else
ringfifo_1[iput_1].frame_type = FRAME_TYPE_P;
iput_1 = addring(iput_1);
n_1++;
}
}
RTSP服务器逻辑之前文章有说过可参考
全套代码下载
边栏推荐
- Oauth2.0 authentication, login and access "/oauth/token", how to get the value of request header authorization (basictoken)???
- Gbase 8C system table PG_ amproc
- [translation] flux is safe. Gain more confidence through fuzzy processing
- How to change the panet layer in yolov5 to bifpn
- sql server 查詢指定錶的錶結構
- Today, it's time to copy the bottom!
- Gbase 8C system table PG_ collation
- Basic operation of binary tree (C language version)
- GBase 8c系统表-pg_conversion
- 左值右指解释的比较好的
猜你喜欢
HTB-Devel
[shutter] banner carousel component (shutter_wiper plug-in | swiper component)
Awk from introduction to earth (0) overview of awk
Oauth2.0 authentication, login and access "/oauth/token", how to get the value of request header authorization (basictoken)???
Random shuffle note
错误Invalid bound statement (not found): com.ruoyi.stock.mapper.StockDetailMapper.xxxx解决
[hcia]no.15 communication between VLANs
Mathematical statistics -- Sampling and sampling distribution
[translation] the background project has joined the CNCF incubator
Xiaodi notes
随机推荐
[fluent] future asynchronous programming (introduction | then method | exception capture | async, await keywords | whencomplete method | timeout method)
Choose it when you decide
GBase 8c 函数/存储过程参数(二)
[principles of multithreading and high concurrency: 1_cpu multi-level cache model]
Interview stereotyped version
Linear rectification function relu and its variants in deep learning activation function
Build a private cloud disk cloudrev
为什么会选择框架?选择什么样的框架
Summary of interview project technology stack
面试八股文整理版
当lambda没有输入时,是何含义?
Restcloud ETL cross database data aggregation operation
JS的装箱和拆箱
What does "where 1=1" mean
定了,就选它
leetcode540
SQL statement
基于can总线的A2L文件解析(2)
Javescript 0.1 + 0.2 = = 0.3 problem
【教程】chrome關閉跨域策略cors、samesite,跨域帶上cookie