从RTP包中解析AAC数据

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

首先上代码:

//rtp拆包成ADTS列表
public static List<byte[]> rtpToAdtsPack(RawPacket rtpPacket){
	//aac码流处理
	if (rtpPacket.getPayloadType() == 96) {												//以下处理仅针对H264码流
		byte[] rtpPayload = rtpPacket.getPayload();
		int aUHeadersLength = ((rtpPayload[0]&0xFF)<<16) + (rtpPayload[1]&0xFF);
		int auCount = aUHeadersLength/16;					//AAC帧的数量
		int index = 2 + auCount*2;							//上一次解析到的位置
		List<byte[]> adtsList = new ArrayList<byte[]>();		//ADTS帧列表
		
		for (int i = 2; i < 2+2*auCount; i+=2) {			//遍历所有auHeader
			int aacDataLen = ((rtpPayload[i]&0xFF)<<5) + ((rtpPayload[i+1]&0xF8)>>3); 		//AAC的数据长度(不包括header)
			byte[] aacData = new byte[aacDataLen];											//AAC的数据
			System.arraycopy(rtpPayload, index, aacData, 0, aacDataLen);
			index += aacDataLen;
			byte[] adts = addAdtsHeader(aacData);
			adtsList.add(adts);
		}
		
		return adtsList;
	}
	return null;
}

//构造ADTS帧,添加ADTS头
public static byte[] addAdtsHeader(byte[] aacData){
	ByteBuffer bb = ByteBuffer.allocate(aacData.length + 7);
	bb.put((byte) 0xFF);		
	bb.put((byte) 0xF1);
	bb.put((byte) 0x60);
	
	short aacFrameLength = (short) ((aacData.length + 7)&0x1FFF);			//取低13位
	bb.put((byte) (((aacFrameLength>>11)&0x0F) + 0x40));
	bb.put((byte) ((aacFrameLength>>3)&0xFF));								//取低3位往后数8位
	bb.put((byte) (((aacFrameLength&0x07)<<5) + 0x02));																//取低3位作为结果的高3位,后5位固定:0 0010
	
	bb.put((byte) 0x40);
	bb.put(aacData);
	return bb.array();
}

 

      从RTP包中解析AAC数据,相对于从RTP包中解析H264数据来说要简单一些,因为AAC的数据帧比较小,不会出现分片封包情况,只有组合封包和单一封包这两种情况。

      首先,我们需要了解AAC音频文件格式。

      AAC音频文件有一帧一帧的ADTS帧组成,每个ADTS帧包含ADTS头部和AAC数据,如下所示

ADTS头部的大小通常为7个字节,包含着这一帧数据的信息,内容如下

 

各字段的意思如下

  • syncword

   总是0xFFF, 代表一个ADTS帧的开始, 用于同步.

  • ID

   MPEG Version: 0 for MPEG-4,1 for MPEG-2

  • Layer

   always: ‘00’

  • protection_absent 

   Warning, set to 1 if there is no CRC and 0 if there is CRC

  • profile

   表示使用哪个级别的AAC,如01 Low Complexity(LC) – AAC LC

  • sampling_frequency_index

   采样率的下标

  

  • aac_frame_length

   一个ADTS帧的长度包括ADTS头和AAC原始流

  • adts_buffer_fullness

   0x7FF 说明是码率可变的码流

  • number_of_raw_data_blocks_in_frame

   表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧

这里主要记住ADTS头部通常为7个字节,并且头部包含aac_frame_length,表示ADTS帧的大小。

 

AAC的RTP打包方式如下:

首先是2个字节的AU-headers-length,因为可以有多个au-header所以AU-headers-length的值是 16的倍数。例如上述例子中AU-headers-length是0x0060,就表示AU-header有6个,每个AU-header占2个字节。

//计算AU-headers-length值

int aUHeadersLength = ((rtpPayload[0]&0xFF)<<16) + (rtpPayload[1]&0xFF);

而AAC data是多个adts里的data合在了一起,需要根据长度来拆解开。并且,有AU-header,就有多少个ADTS帧。

AU-header中高13个bits就是一个au 的字节长度,只要获取到该长度,就可以从RTP包中解析出AAC的data,然后再添加一个ADTS头,存储到文件中即可。

//计算每一个AAC data的长度

int aacDataLen = ((rtpPayload[i]&0xFF)<<5) + ((rtpPayload[i+1]&0xF8)>>3);         //AAC的数据长度(不包括header)

例如,上面例子中,第一个AU-header是05 C8,换成2进制是0000 0101 1100 1000,取前13位是0000 0101 1100 1,换算成十进制就是185,也就是第一个AAC data的长度是185。

取出AAC data添加一个ADTS头,再存入文件中。

int aacDataLen = ((rtpPayload[i]&0xFF)<<5) + ((rtpPayload[i+1]&0xF8)>>3);         //AAC的数据长度(不包括header)
byte[] aacData = new byte[aacDataLen];                                            //AAC的数据
System.arraycopy(rtpPayload, index, aacData, 0, aacDataLen);
index += aacDataLen;
byte[] adts = addAdtsHeader(aacData);

 

关于添加一个ADTS头,这里有两个地方需要注意的,一个是采样率,一个是ADTS帧长度。我这里采样率采用的是0x8(16000),

而ADTS帧长度存储在倒数13位往前数13位,跨越了3个字节,计算的时候稍微有一点点麻烦。

short aacFrameLength = (short) ((aacData.length + 7)&0x1FFF);            //取低13位
bb.put((byte) (((aacFrameLength>>11)&0x0F) + 0x40));
bb.put((byte) ((aacFrameLength>>3)&0xFF));                                //取低3位往后数8位
bb.put((byte) (((aacFrameLength&0x07)<<5) + 0x02));                  //取低3位作为结果的高3位,后5位固定:0 0010

其他字段我采用的是固定格式,没有传参数进来

ByteBuffer bb = ByteBuffer.allocate(aacData.length + 7);
bb.put((byte) 0xFF);        
bb.put((byte) 0xF1);
bb.put((byte) 0x60);

目前的代码都经过了测试,是可用的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值