rtp的视频和音频格式初步分析(golang解析)

rtp视频、音频格式解析

一、rtp承载h264解析

rtp承载h264的解析当前实现了两种方式:StapA和FuA

NALU头由一个字节组成,它的语法如下:

rtp的视频和音频格式初步分析(golang解析)_第1张图片
* 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 包中.

rtp的视频和音频格式初步分析(golang解析)_第2张图片
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) 
startIndex = startIndex + NALU_SIZE + naluSize



if startIndex >= (len(packet) - 1) {

break

}

}

2. FuA

FuA是当H264报文比较大,拆分成多个后放在多个rtp封装中。

别被名字吓到这个格式就是上面提到的RTP h264负载类型,Type为FU-A

rtp的视频和音频格式初步分析(golang解析)_第3张图片
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
for _, auLen := range auLenArray {
retPacket := &AVPacket{}



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)

}

你可能感兴趣的:(golang,音视频)