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

从RTP包中解析AAC数据

程序员文章站 2022-07-07 11:26:14
...

源码地址: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数据,如下所示

从RTP包中解析AAC数据

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

从RTP包中解析AAC数据

 

从RTP包中解析AAC数据

各字段的意思如下

  • 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

   采样率的下标

  从RTP包中解析AAC数据

  • 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打包方式如下:

从RTP包中解析AAC数据

从RTP包中解析AAC数据

首先是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);

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

相关标签: 音视频开发