从RTP包中解析H265数据

源码地址: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包只包含一个NALU

2、分片封包

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

3、组合封包模式

 

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

 

 

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

1、组合封包例子

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

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

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

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

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

 

2、分片封包例子

下面是分片封包的例子

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

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

1001 0011

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

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

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

 

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值