【富瀚6630编码存录像,用rtsp服务器及时间戳同步实现vlc观看录像】

代码逻辑主要讲几个重要的函数

编码线程_thread_venc_data(h264编码)

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服务器逻辑之前文章有说过可参考

点这点这~~~

全套代码下载

点这点这~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I&You

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值