rtp视频、音频格式解析
一、rtp承载h264解析
rtp承载h264的解析当前实现了两种方式:StapA和FuA
NALU头由一个字节组成,它的语法如下:
* F: 1个比特.
forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
* NRI: 2个比特.
nal_ref_idc. 取00~11,似乎指示这个NALU的重要性,如00的NALU解码器
可以丢弃它而不影响图像的回放.
* Type: 5个比特.
nal_unit_type. 这个NALU单元的类型.简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
golang的区分示例代码为:
if naluType == 0x18 { //STAP-A
self.packetMutex.Lock()
packetArray = self.demuxStapA(packet.data)
self.packetMap.Remove(int(packet.sn))
self.packetMutex.Unlock()
} else if naluType == 0x1c { //FUs
//log.Infof(“getPacket start demuxFUa…”)
retAvPacket := self.demuxFUa()
if retAvPacket != nil {
packetArray = append(packetArray, retAvPacket)
}
}
1. StapA
简单描述,就是一个报文中包含多个h264报文。
当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.
golang解析stapa的代码示例:
for {
naluPacket := &AVPacket{}
naluData := packet[startIndex:]
naluSize := int(pio.U16BE(naluData))
endPos := startIndex + NALU_SIZE + naluSize
if endPos >= (len(packet) - 1) {
break
}
naluhdr := packet[startIndex+NALU_SIZE]
if naluhdr == NALU_ACCESS {
startIndex = startIndex + NALU_SIZE + naluSize
continue
}
naluPacket.data = append(naluPacket.data, packet[startIndex+NALU_SIZE:endPos]…)
naluPacket.isVideo = true
packetArray = append(packetArray, naluPacket) <br/>
startIndex = startIndex + NALU_SIZE + naluSize <br/>
if startIndex >= (len(packet) - 1) {
break
}
}
2. FuA
FuA是当H264报文比较大,拆分成多个后放在多个rtp封装中。
别被名字吓到这个格式就是上面提到的RTP h264负载类型,Type为FU-A
S bit为1表示分片的NAL开始,当它为1时,E不能为1
E bit为1表示结束,当它为1,S不能为1
R bit保留位
Type就是NALU头中的Type,取1-23的那个值
所以,需要把fu indicator和fu header进行拼接:naluHeader := (fuIndicator & 0xe0) | (fuHeader & 0x1f)
golang的实例代码为:
for index, key := range seqArray {
seq := key.(int)
if lastSeq == 0 {
lastSeq = seq
} else {
if (seq - lastSeq) != 1 {
discardFlag = true
}
lastSeq = seq
}
var packet *RtpPacket
if !discardFlag {
value, _ := self.packetMap.Get(seq)
packet = value.(*RtpPacket)
avpacket.payloadtype = int(packet.pt)
avpacket.timestamp = packet.timestamp * 1000 / 90000
fuIndicator := packet.data[0]
fuHeader := packet.data[1]
naluHeader := (fuIndicator & 0xe0) | (fuHeader & 0x1f)
if index == 0 {
avpacket.data = append(avpacket.data, naluHeader)
}
avpacket.data = append(avpacket.data, packet.data[2:]…)
}
self.packetMap.Remove(seq)
if packet.m != 0 && index != 0 {
break
}
}
二、音频aac
rtp承载aac的格式由两部分组成:
* 2个字节的AU-headers-length
* n个AU-header,每个2字节
* n个AU,是aac去掉adts的载荷
1. AU-headers-length
头两个字节表示au-header的长度,单位是bit。
golang代码示例:
auHeaderLen := pio.U16BE(packet.data[0:])
auHeaderLen = auHeaderLen / 8
auHeaderCount := int(auHeaderLen / 2)
因为单位是bit, 除以8就是auHeader的字节长度;又因为单个auheader字节长度2字节,所以再除以2就是auheader的个数。
2. AU-header
au-header的高13个bits就是一个au的字节长度:
golang示例:
for iIndex = 0; iIndex < int(auHeaderCount); iIndex++ {
auHeaderInfo := pio.U16BE(packet.data[2+2*iIndex:])
auLen := auHeaderInfo >> 3
auLenArray = append(auLenArray, int(auLen))
}
这样就能得到多个au的长度
3. AU
startOffset := 2 + 2*auHeaderCount<br/>
for _, auLen := range auLenArray {<br/>
retPacket := &AVPacket{}<br/>
adtsPacket := self.GetADTS(self.sampleRate, self.channel, self.aacProfile, auLen)
retPacket.data = append(retPacket.data, adtsPacket[0:7]…)
endOffset := startOffset + auLen
retPacket.data = append(retPacket.data, packet.data[startOffset:endOffset]…)
retPacket.isVideo = false
retPacket.timestamp = packet.timestamp * 1000 / 90000
retPacket.payloadtype = int(packet.pt)
startOffset = startOffset + auLen
retPacketArray = append(retPacketArray, retPacket)
}