当前位置:网站首页>【富瀚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服务器逻辑之前文章有说过可参考
全套代码下载
边栏推荐
- Memory pool (understand the process of new developing space from the perspective of kernel)
- 简单理解svg
- What is the way out for children from poor families?
- random shuffle注意
- Gbase 8C function / stored procedure parameters (I)
- 二维格式数组格式索引下标连续问题导致 返回json 格式问题
- [shutter] bottom navigation bar page frame (bottomnavigationbar bottom navigation bar | pageview sliding page | bottom navigation and sliding page associated operation)
- Unity3d human skin real time rendering real simulated human skin real time rendering "suggestions collection"
- [translation] flux is safe. Gain more confidence through fuzzy processing
- The Linux server needs to install the agent software EPS (agent) database
猜你喜欢

Tongda OA V12 process center

超好用的日志库 logzero

The Linux server needs to install the agent software EPS (agent) database
![ASP. Net core 6 framework unveiling example demonstration [02]: application development based on routing, MVC and grpc](/img/cb/145937a27ef08050a370d5a255215a.jpg)
ASP. Net core 6 framework unveiling example demonstration [02]: application development based on routing, MVC and grpc

How to change the panet layer in yolov5 to bifpn

Principle and application of database

where 1=1 是什么意思

Awk from introduction to earth (0) overview of awk

搭建私有云盘 cloudreve

Matlab tips (24) RBF, GRNN, PNN neural network
随机推荐
[fluent] JSON model conversion (JSON serialization tool | JSON manual serialization | writing dart model classes according to JSON | online automatic conversion of dart classes according to JSON)
Choose it when you decide
GBase 8c系统表-pg_am
Codeforces Round #418 (Div. 2) D. An overnight dance in discotheque
[tutorial] chrome turns off cross domain policies CORS and samesite, and brings cookies across domains
Error invalid bound statement (not found): com ruoyi. stock. mapper. StockDetailMapper. XXXX solution
GBase 8c系统表-pg_amproc
The solution of "the required function is not supported" in win10 remote desktop connection is to modify the Registry [easy to understand]
Why choose a frame? What frame to choose
GBase 8c系统表-pg_authid
GBase 8c系统表pg_database
GBase 8c系统表-pg_constraint
Practice of traffic recording and playback in vivo
2022-2028 global splicing display industry research and trend analysis report
sql server数据库添加 mdf数据库文件,遇到的报错
"Analysis of 43 cases of MATLAB neural network": Chapter 43 efficient programming skills of neural network -- Discussion Based on the characteristics of the new version of MATLAB r2012b
Serious security vulnerabilities reported by moxa mxview network management software
MATLAB小技巧(24)RBF,GRNN,PNN-神经网络
Gbase 8C system table PG_ constraint
Gbase 8C system table PG_ collation