欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从RTP包中解析H265数据

程序员文章站 2022-07-14 18:48:05
...

源码地址:https://github.com/zhouyinfei/rtsp-netty-server

 

首先上代码:

//rtp拆包成nalu h265
public static byte[] rtpToNaluH265Pack(RawPacket rtpPacket){
	//h265码流处理
//		if (rtpPacket.getPayloadType() == 96) {												//以下处理仅针对H265码流
	ByteBuffer bb = null;														//存放RTP解析后的NALU的数据
	
	byte[] rtpPayload = rtpPacket.getPayload();
	byte fu_header0 = rtpPayload[0];							
	byte nalu_type = (byte) ((fu_header0>>1) & 0x3f);			//获取NALU TYPE
	  
//			System.out.println("nalu_type=" + nalu_type);
	if (nalu_type == 49) {  										//分片封包模式
		byte fu_header2 = rtpPayload[2];
		byte start_flag = (byte) (fu_header2 & 0x80);				//是否起始片
		byte end_flag = (byte) (fu_header2 & 0x40);					//是否结束片
		nalu_type = (byte) (fu_header2&0x3F);						//nalu type
		byte nalu_header0 = (byte) (nalu_type<<1);					
		byte nalu_header1 = 0x01;										//固定值
		if (start_flag != 0) {											//第一个分片
			bb = ByteBuffer.allocate(rtpPayload.length + 3);
			bb.put(new byte[]{0x0, 0x0, 0x0, 0x1});
			bb.put(nalu_header0);
			bb.put(nalu_header1);
			byte[] dest = new byte[rtpPayload.length-3];
			System.arraycopy(rtpPayload, 3, dest, 0, rtpPayload.length-3);
			bb.put(dest);
		} else if (end_flag != 0) {										//最后一个分片
			bb = ByteBuffer.allocate(rtpPayload.length-3);
			byte[] dest = new byte[rtpPayload.length-3];
			System.arraycopy(rtpPayload, 3, dest, 0, rtpPayload.length-3);
			bb.put(dest);
		} else {															//中间分片
			bb = ByteBuffer.allocate(rtpPayload.length-3);
			byte[] dest = new byte[rtpPayload.length-3];
			System.arraycopy(rtpPayload, 3, dest, 0, rtpPayload.length-3);
			bb.put(dest);
		}
	} else if (nalu_type == 48) {  								//组合封包模式
		int srcOffset = 2;										//第一个字节是STAP-A头,跳过
		int bufferLen = 0;
		//先计算需要的ByteBuffer长度,再将内容放进去
		while ((rtpPayload.length - srcOffset) > 2)				//循环解析RTP,将组合后的NALU取出来,再加上起始码
		{	
			int size = 0;										//NALU的长度,2个字节
			size |= rtpPayload[srcOffset] << 8;						
			size |= rtpPayload[srcOffset + 1];

			srcOffset += 2;										//将NALU header和NALU payload一起放进去,然后进入下一个循环
			bufferLen += (4+size);
			srcOffset += size;
		}
		
		srcOffset = 2;
		bb = ByteBuffer.allocate(bufferLen);
		while ((rtpPayload.length - srcOffset) > 2)				//循环解析RTP,将组合后的NALU取出来,再加上起始码
		{	
			int size = 0;										//NALU的长度,2个字节
			size |= rtpPayload[srcOffset] << 8;						
			size |= rtpPayload[srcOffset + 1];

			srcOffset += 2;										//将NALU header和NALU payload一起放进去,然后进入下一个循环
			byte[] dest = new byte[size];
			System.arraycopy(rtpPayload, srcOffset, dest, 0, size);
			
			bb.put(new byte[]{0x0, 0x0, 0x0, 0x1});				//NALU的起始码
			bb.put(dest);
			
			srcOffset += size;
		}
		
	 } else if (nalu_type == 1 || nalu_type == 19 || nalu_type == 32 || nalu_type == 33 ||
			 nalu_type == 34 || nalu_type == 39) {											//单一NAL 单元模式
		  bb = ByteBuffer.allocate(rtpPayload.length + 4);					//将整个rtpPayload一起放进去
		  bb.put(new byte[]{0x0, 0x0, 0x0, 0x1});
		  bb.put(rtpPayload);
	 } else {
		 log.debug("rtpToNaluH265Pack-----Unsupport nalu type!");
	 }
	
	if (bb != null) {
		return bb.array();
	}
//		}
	return null;
}

首先是H265的格式,参考:

https://blog.csdn.net/weixin_42226021/article/details/88936803

https://blog.csdn.net/g0415shenw/article/details/81609261

 

这里主要需要关注H265封包成RTP的部分:

(1)、一个NALU打包成一个RTP包,只需要在一个12字节的RTP包头后添加去掉开始码的NALU即可
(这种模式在一个NALU的大小小于MTU时使用)。
(2)、一个NALU打包成几个RTP包(FUs模式),在12个字节的RTP头后面有两个字节的PayloadHdr和一个字节的FU
header。PayloadHdr的值等于NALU头的type位改为49(十进制)后的值,FU header第1位标记RTP包是否为NALU的第一片,第2位标记RTP包是否为NALU的最后一片。后6位是NALU头的type位。

从上面说明大概可知,H265的封包模式如下:

1、单一单元模式

从RTP包中解析H265数据

一个RTP包只包含一个NALU

2、分片封包

从RTP包中解析H265数据

FU header还包括了是否是第一片或者最后一片的标识。

3、组合封包模式

从RTP包中解析H265数据

 

这篇博客没有说明组合封包模式,但是根据 实验发现,组合封包模式的时候,type的值是48。

 

 

下面举例子对第二、三种情况进行说明。

1、组合封包例子

例如如下的H265封装后的RTP包

从RTP包中解析H265数据

前2个字节是PayloadHdr,内容是:60 01,其格式如下:

从RTP包中解析H265数据

所以要获取1-6(从0开始数)位上的内容,结果是48,说明是组合封包模式。

接下来2个字节是NALU的长度00 17,标识了NALU的长度是23。

从RTP包中解析H265数据

依次类推,第二个NALU的长度是00 22,也就是34,然后后面的34个字节是第二个NALU的内容。

从RTP包中解析H265数据

 

2、分片封包例子

下面是分片封包的例子

从RTP包中解析H265数据

前2个字节是62 01,根据格式获取到type的值是49,所以可知是分片封包类型。

接下来的1个字节是FU header,值是93。

1001 0011

其中后6位是NALU type,可以知道NALU type是19,属于IDR类型。而第一位是1,所以它是第一个分片。

看一下最后一个分片的格式:

从RTP包中解析H265数据

第3字节是53,二进制格式为:0101 0011, NALU type是19, 第二位是1,所以是最后一个分片。

 

相关标签: 音视频开发