You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/rtprtcp/rtp_unpacker_avc.go

251 lines
6.8 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package rtprtcp
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
func calcPositionIfNeededAVC(pkt *RTPPacket) {
b := pkt.Raw[pkt.Header.payloadOffset:]
// rfc3984 5.3. NAL Unit Octet Usage
//
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
outerNALUType := b[0] & 0x1F
if outerNALUType <= NALUTypeAVCSingleMax {
pkt.positionType = PositionTypeSingle
return
} else if outerNALUType == NALUTypeAVCFUA {
// rfc3984 5.8. Fragmentation Units (FUs)
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | FU indicator | FU header | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// | FU payload |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// FU indicator:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |F|NRI| Type |
// +---------------+
//
// Fu header:
// +---------------+
// |0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+
// |S|E|R| Type |
// +---------------+
fuIndicator := b[0]
_ = fuIndicator
fuHeader := b[1]
startCode := (fuHeader & 0x80) != 0
endCode := (fuHeader & 0x40) != 0
if startCode {
pkt.positionType = PositionTypeFUAStart
return
}
if endCode {
pkt.positionType = PositionTypeFUAEnd
return
}
pkt.positionType = PositionTypeFUAMiddle
return
} else if outerNALUType == NALUTypeAVCSTAPA {
pkt.positionType = PositionTypeSTAPA
} else {
nazalog.Errorf("unknown nalu type. outerNALUType=%d", outerNALUType)
}
return
}
// AVC或HEVC格式的流尝试合成一个完整的帧
func (r *RTPUnpacker) unpackOneAVCOrHEVC() bool {
first := r.list.head.next
if first == nil {
return false
}
switch first.packet.positionType {
case PositionTypeSingle:
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
pkt.Payload = make([]byte, len(first.packet.Raw)-int(first.packet.Header.payloadOffset)+4)
bele.BEPutUint32(pkt.Payload, uint32(len(first.packet.Raw))-first.packet.Header.payloadOffset)
copy(pkt.Payload[4:], first.packet.Raw[first.packet.Header.payloadOffset:])
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeSTAPA:
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = first.packet.Header.Timestamp / uint32(r.clockRate/1000)
// 跳过首字节并且将多nalu前的2字节长度替换成4字节长度
buf := first.packet.Raw[first.packet.Header.payloadOffset+1:]
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
for i := 0; i != len(buf); {
if len(buf)-i < 2 {
nazalog.Errorf("invalid STAP-A packet.")
return false
}
naluSize := int(bele.BEUint16(buf[i:]))
totalSize += 4 + naluSize
i += 2 + naluSize
}
pkt.Payload = make([]byte, totalSize)
j := 0
for i := 0; i != len(buf); {
naluSize := int(bele.BEUint16(buf[i:]))
bele.BEPutUint32(pkt.Payload[j:], uint32(naluSize))
copy(pkt.Payload[j+4:], buf[i+2:i+2+naluSize])
j += 4 + naluSize
i += 2 + naluSize
}
r.unpackedFlag = true
r.unpackedSeq = first.packet.Header.Seq
r.list.head.next = first.next
r.list.size--
r.onAVPacket(pkt)
return true
case PositionTypeFUAStart:
prev := first
p := first.next
for {
if prev == nil || p == nil {
return false
}
if SubSeq(p.packet.Header.Seq, prev.packet.Header.Seq) != 1 {
return false
}
if p.packet.positionType == PositionTypeFUAMiddle {
prev = p
p = p.next
continue
} else if p.packet.positionType == PositionTypeFUAEnd {
var pkt base.AVPacket
pkt.PayloadType = r.payloadType
pkt.Timestamp = p.packet.Header.Timestamp / uint32(r.clockRate/1000)
var naluTypeLen int
var naluType []byte
if r.payloadType == base.AVPacketPTAVC {
naluTypeLen = 1
naluType = make([]byte, naluTypeLen)
fuIndicator := first.packet.Raw[first.packet.Header.payloadOffset]
fuHeader := first.packet.Raw[first.packet.Header.payloadOffset+1]
naluType[0] = (fuIndicator & 0xE0) | (fuHeader & 0x1F)
} else {
naluTypeLen = 2
naluType = make([]byte, naluTypeLen)
buf := first.packet.Raw[first.packet.Header.payloadOffset:]
fuType := buf[2] & 0x3f
naluType[0] = (buf[0] & 0x81) | (fuType << 1)
naluType[1] = buf[1]
}
// 使用两次遍历,第一次遍历找出总大小,第二次逐个拷贝,目的是使得内存块一次就申请好,不用动态扩容造成额外性能开销
totalSize := 0
pp := first
for {
totalSize += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - (naluTypeLen + 1)
if pp == p {
break
}
pp = pp.next
}
pkt.Payload = make([]byte, totalSize+4+naluTypeLen)
bele.BEPutUint32(pkt.Payload, uint32(totalSize+naluTypeLen))
var index int
if r.payloadType == base.AVPacketPTAVC {
pkt.Payload[4] = naluType[0]
index = 5
} else {
pkt.Payload[4] = naluType[0]
pkt.Payload[5] = naluType[1]
index = 6
}
packetCount := 0
pp = first
for {
copy(pkt.Payload[index:], pp.packet.Raw[int(pp.packet.Header.payloadOffset)+(naluTypeLen+1):])
index += len(pp.packet.Raw) - int(pp.packet.Header.payloadOffset) - (naluTypeLen + 1)
packetCount++
if pp == p {
break
}
pp = pp.next
}
r.unpackedFlag = true
r.unpackedSeq = p.packet.Header.Seq
r.list.head.next = p.next
r.list.size -= packetCount
r.onAVPacket(pkt)
return true
} else {
// 不应该出现其他类型
nazalog.Errorf("invalid position type. position=%d", p.packet.positionType)
return false
}
}
case PositionTypeFUAMiddle:
// noop
case PositionTypeFUAEnd:
// noop
default:
nazalog.Errorf("invalid position. pos=%d", first.packet.positionType)
}
return false
}