低延时高RTSP兼容的EasyPlayerRTSPwin解决H.264一帧多个nal单元录像花屏问题方案

摘要:
在实际的H264数据帧中,往往帧前面带有00000001或000001分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧……=NULL){unsignedcharnaltype=;if//0x67{//m_psps=pout;//m_spslen=outlen;//pout[0]=0x67;if{m_psps=newunsignedchar[outlen];memcpy;m_spslen=outlen;}bSPSOrPPS=true;}elseif//0x68{//m_ppps=pout;//m_ppslen=outlen;//pout[0]=0x68;if{m_ppps=newunsignedchar[outlen];//outlenmemcpy;m_ppslen=outlen;}bSPSOrPPS=true;}//elseif//SEI//{////}//else{memcpy;//写入头4个字节==nal内容的长度unsignedcharbyte0=pRealData[nRealDataSize+3];unsignedcharbyte1=pRealData[nRealDataSize+2];pRealData[nRealDataSize+3]=pRealData[nRealDataSize+0];pRealData[nRealDataSize+2]=pRealData[nRealDataSize+1];pRealData[nRealDataSize+1]=byte1;pRealData[nRealDataSize+0]=byte0;nRealDataSize+=4;memcpy;nRealDataSize+=outlen;}inlen=inlen-outlen-;pin=pout+outlen;}}while(bend!if{delete[]pRealData;pRealData=NULL;}returntrue;}其中find_nal_unit()函数是从H264帧中分析出以00000001和000001开头的NAL单元,然后直接填充成该NAL单元的长度,注意字节顺序为大端顺序://写入头4个字节==nal内容的长度unsignedcharbyte0=pRealData[nRealDataSize+3];unsignedch

EasyPlayer-RTSP-win解决H264一帧多个nal单元录像花屏问题

我们来讲解一下关于H264编码格式中的一帧多nal(Network Abstract Layer, 即网络抽象层),关于H264和NAL,这里引用一段话来科普一下:

【转】 在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧……

一般情况下,一个H264帧直接以00 00 00 01开头作为一个NAL作为网络传输单元,而在有些H264的编码器则编码出来的H264帧包含了多个NAL,这个时候每个分片的NAL(注意是分片的)则是是以00 00 01开头作为网络传输单元,经过分片的NAL数据量更小,从而更加方便进行网络;但是,我们在接收到带有多个NAL的H264帧的时候进行写MP4则不能简单是只通过将头部的00 00 00 01标志转换从AVC的长度标识,而需要将所有的00 00 00 01和00 00 01都需要转换成该NAL单元的长度,否则就会出现视频解码只能播放头部一小部分,其他部分全部花屏的情况,如下图所示:

低延时高RTSP兼容的EasyPlayerRTSPwin解决H.264一帧多个nal单元录像花屏问题方案第1张

说了这么多,大家是否明白了呢,如果不明白的(文字描述比较虚),我们直接看EasyPlayer代码实现:

int EasyMP4Writer::WriteMp4File(unsigned char* pdata, int datasize, bool keyframe, long nTimestamp, int nWidth, int nHeight)
{
	if (nTimestamp==0||(pdata==NULL)||datasize<=0)
	{
		return -1;
	}
	int inlen=datasize;
	unsigned char*pin=pdata;
	int outlen=0;
	unsigned char*pout=NULL;
	bool bend = false;

	int datalen=0;
	bool bSPSOrPPS = false;

	int iOutLen = datasize;

	unsigned char* pRealData = new unsigned char[datasize<<1];
	int nRealDataSize = 0;
	memset(pRealData,0x00, datasize+4);
	do 
	{
		int nal_start = 0;
		int nal_end = 0;
		outlen = find_nal_unit(pin,inlen, &nal_start, &nal_end );
		if (outlen<=0)
		{
			break;
		}
		pout = pin+nal_start;
		if(pout!=NULL)
		{
			unsigned char naltype = ( pout[0] & 0x1F);

			if (naltype==0x07)//0x67
			{
// 				m_psps=pout;
// 				m_spslen=outlen;
				//pout[0] = 0x67;
				if(m_bwritevideoinfo==false)
				{
					m_psps = new unsigned char[outlen];
					memcpy(m_psps, pout, outlen);
					m_spslen=outlen;
				}
				bSPSOrPPS = true;
			}
			else if (naltype==0x08)//0x68
			{
				// 				m_ppps=pout;
				// 				m_ppslen=outlen;
				//pout[0] = 0x68;
				if(m_bwritevideoinfo==false)
				{
					m_ppps = new unsigned char[outlen];//outlen
					memcpy(m_ppps, pout, outlen);
					m_ppslen = outlen;
				}
				bSPSOrPPS = true;
			}
// 			else if (pout[0] == 0x06)//SEI
// 			{
// 
// 			}
//			else
			{
				memcpy(pRealData+nRealDataSize, &outlen, 4);
				//写入头4个字节==nal内容的长度(H264数据的长度)
				unsigned char byte0 = pRealData[nRealDataSize+3];
				unsigned char byte1 = pRealData[nRealDataSize+2];
				pRealData[nRealDataSize+3] = pRealData[nRealDataSize+0];
				pRealData[nRealDataSize+2] = pRealData[nRealDataSize+1];
				pRealData[nRealDataSize+1] = byte1;
				pRealData[nRealDataSize+0] = byte0;

				nRealDataSize += 4;

				memcpy(pRealData+nRealDataSize, pout, outlen);
				nRealDataSize += outlen;
			}

			inlen=inlen-outlen-(pout-pin);
			pin=pout+outlen;
		}
	} while (bend!=true);

	if (m_bwritevideoinfo==false&&m_ppps&&m_psps)
	{
		// PPS末尾的0过滤,否则VLC可能播放不出来 [12/22/2015 Dingshuai]
		int nPPSSize = m_ppslen;
		int nZeroCount = 0;
		for (int nI = nPPSSize-1; nI>=0; nI--)
		{
			if (m_ppps[nI] == 0x00)
			{
				nZeroCount++;
			}
			else
			{
				break;
			}
		}
		m_ppslen = m_ppslen-nZeroCount;
		WriteH264SPSandPPS(m_psps,m_spslen,m_ppps,m_ppslen,nWidth,nHeight);
		m_bwritevideoinfo = true;
	}
	if (m_bwritevideoinfo==false||nRealDataSize<=0 )
	{
		return 0;//获取sps pps失败
	}

// 	if(/*bSPSOrPPS*/pout[0]==0x67 || pout[0]==0x68)
// 	{
// 		return 0;
// 	}
	WriteH264Frame(pRealData, nRealDataSize, keyframe, nTimestamp);//左移4单位,加上数据长度头?

	if (pRealData)
	{	
		delete []pRealData;
		pRealData = NULL;
	}

	return true;
}

其中find_nal_unit()函数是从H264帧中分析出以00 00 00 01和00 00 01开头的NAL单元,然后直接填充成该NAL单元的长度,注意字节顺序为大端顺序:
//写入头4个字节==nal内容的长度(H264数据的长度)

unsigned char byte0 = pRealData[nRealDataSize+3];
unsigned char byte1 = pRealData[nRealDataSize+2];
pRealData[nRealDataSize+3] = pRealData[nRealDataSize+0];
pRealData[nRealDataSize+2] = pRealData[nRealDataSize+1];
pRealData[nRealDataSize+1] = byte1;
pRealData[nRealDataSize+0] = byte0;

将NAL长度拷贝到AVC的缓冲区内,紧接着数据部分拷贝:
memcpy(pRealData+nRealDataSize, pout, outlen);
最后,组装成完成的avc帧之后写入MP4,播放的时候就不会有花屏、马赛克的情况出现了。

免责声明:文章转载自《低延时高RTSP兼容的EasyPlayerRTSPwin解决H.264一帧多个nal单元录像花屏问题方案》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Electron remote 模块、通过 BrowserWindow 打开新窗口微信小游戏的本地缓存和清除的使用 (text.js image.js file-util.js)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

海康摄像头web观看操作rtmp/hls推流rtsp流转rtmp流

源码介绍:   海康摄像头web端无插件播放是指设置好摄像头参数后,即可在web浏览器不安装任何自定义插件即可实现流程播放。   系统通过拉取海康rtsp流,在用户点击观看时,自动将流转换为rtmp与hls流,推送到自定义流媒体服务器,实现web端 flash播放与html5原生播放。  如有需求,可在线留言或者发消息都可以,看到后会第一时间回复。  ...

高起播低延时RTSP网页无插件流媒体播放器EasyPlayerRTSP插件使用说明

鉴于大家对于EasyPlayer-RTSP插件的使用还不太熟悉,特此写一篇插件的使用文档,供大家参考;EasyPlayer-RTSP插件有两种,一种是基于IE的ActiveX控件,一种是基于FireFox(也支持多浏览器)的npAPI插件; 两种插件均包含源码,由于FireBreath框架生成代码比较多,npAPI插件只包含关键代码,后续会提供专门讲解Fi...

RTSP实例解析

核心提示:rtsp简介(ZT) Real Time Streaming Protocol或者RTSP(实时流媒体协议),是由Real network 和 Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议。RTSP提供一 种可扩展的框架,使能够提供能控制的,按需传输实时数据,比如音频和视频文件 rtsp简介(ZT)Real Time...

海康威视 嵌入到web页面相关问题

  因项目上面需求,接触到了海康威视的设备,要把他嵌入到web页面中,所以就开始了爬坑之路   刚开始不知道如何把设备的页面嵌入到项目的web页面中,直到我遇见了他——“WEB3.0控件开发包V1.0.9_CN” (感谢网友的无私奉献)   这个插件如果用chrome高版本的话 会提示 您的chrome浏览器版本过高,不支持NPAPI插件    如果出现这...

华为、科达、海康、大华等厂家摄像头通过非标方式(RTSP)接入流媒体服务实现WEB直播与录像

LiveNVR Onvif/RTSP流媒体服务,支持RTSP稳定拉流接入,支持Onvif协议接入,支持RTMP/HLS/HTTP-FLV分发,将传统安防监控设备互联化,无插件直播等。 什么是视频广场 所有配置接入的海康、大华的摄像机,通过统一页面展示,可以展示摄像机的最新快照,在线信息,当用户需要观看的时候,点击播放即可方便快捷。 点击观看效果 多种流的...

ffmpeg文档24-协议

24 协议 FFmpeg协议配置元素,用于访问资源时要求特定的协议。 默认编译时会自动支持所有可用协议。你可以在编译脚本中添加 "–list-protocols"选项来了解有哪些协议被支持。 你也可以在编译时通过 "–disable-protocols"禁止所有的协议支持,然后通过 "–enable-protocol=PROTOCOL"来启用个别协议,或...