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_packet.go

269 lines
6.7 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 2021, 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/avc"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/naza/pkg/bele"
)
// -----------------------------------
// rfc3550 5.1 RTP Fixed Header Fields
// -----------------------------------
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |V=2|P|X| CC |M| PT | sequence number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | timestamp |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | synchronization source (SSRC) identifier |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
// | contributing source (CSRC) identifiers |
// | .... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
const (
RtpFixedHeaderLength = 12
DefaultRtpVersion = 2
)
const (
PositionTypeSingle uint8 = 1
PositionTypeFuaStart uint8 = 2
PositionTypeFuaMiddle uint8 = 3
PositionTypeFuaEnd uint8 = 4
PositionTypeStapa uint8 = 5 // 1个rtp包包含多个帧目前供h264的stapa使用
PositionTypeAp uint8 = 6 // 1个rtp包包含多个帧目前供h265的ap使用
)
type RtpHeader struct {
Version uint8 // 2b *
Padding uint8 // 1b
Extension uint8 // 1
CsrcCount uint8 // 4b
Mark uint8 // 1b *
PacketType uint8 // 7b
Seq uint16 // 16b **
Timestamp uint32 // 32b **** samples
Ssrc uint32 // 32b **** Synchronization source
Csrc []uint32
ExtensionProfile uint16
// Extensions 包含了整个extension引用的是包体的内存
//
// TODO(chef): [opt] 后续考虑解析extension中的单独个item存储至结构体中 202211
Extensions []byte
payloadOffset uint32 // body部分真正数据部分的起始位置
paddingLength int // 末尾padding的长度
}
type RtpPacket struct {
Header RtpHeader
Raw []byte // 包含header内存
positionType uint8
}
func (h *RtpHeader) PackTo(out []byte) {
out[0] = h.CsrcCount | (h.Extension << 4) | (h.Padding << 5) | (h.Version << 6)
out[1] = h.PacketType | (h.Mark << 7)
bele.BePutUint16(out[2:], h.Seq)
bele.BePutUint32(out[4:], h.Timestamp)
bele.BePutUint32(out[8:], h.Ssrc)
// TODO(chef): pack csrc 202210
}
func MakeDefaultRtpHeader() RtpHeader {
return RtpHeader{
Version: DefaultRtpVersion,
Padding: 0,
Extension: 0,
CsrcCount: 0,
payloadOffset: RtpFixedHeaderLength,
}
}
func MakeRtpPacket(h RtpHeader, payload []byte) (pkt RtpPacket) {
pkt.Header = h
pkt.Raw = make([]byte, RtpFixedHeaderLength+len(payload))
pkt.Header.PackTo(pkt.Raw)
copy(pkt.Raw[RtpFixedHeaderLength:], payload)
return
}
func ParseRtpHeader(b []byte) (h RtpHeader, err error) {
if len(b) < RtpFixedHeaderLength {
return h, base.ErrRtpRtcpShortBuffer
}
h.Version = b[0] >> 6
h.Padding = (b[0] >> 5) & 0x1
h.Extension = (b[0] >> 4) & 0x1
h.CsrcCount = b[0] & 0xF
h.Mark = b[1] >> 7
h.PacketType = b[1] & 0x7F
h.Seq = bele.BeUint16(b[2:])
h.Timestamp = bele.BeUint32(b[4:])
h.Ssrc = bele.BeUint32(b[8:])
offset := RtpFixedHeaderLength
if h.CsrcCount > 0 {
h.Csrc = make([]uint32, h.CsrcCount)
}
for i := uint8(0); i < h.CsrcCount; i++ {
if offset+4 > len(b) {
return h, base.ErrRtpRtcpShortBuffer
}
h.Csrc[i] = bele.BeUint32(b[offset:])
offset += 4
}
if h.Extension != 0 {
if offset+4 > len(b) {
return h, base.ErrRtpRtcpShortBuffer
}
// rfc3550#section-5.3.1
h.ExtensionProfile = bele.BeUint16(b[offset:])
offset += 2
extensionLength := bele.BeUint16(b[offset:])
offset += 2
h.Extensions = b[offset : offset+int(extensionLength)]
}
if offset >= len(b) {
return h, base.ErrRtpRtcpShortBuffer
}
h.payloadOffset = uint32(offset)
if h.Padding == 1 {
h.paddingLength = int(b[len(b)-1])
}
return
}
// ParseRtpPacket 函数调用结束后,不持有参数<b>的内存块
func ParseRtpPacket(b []byte) (pkt RtpPacket, err error) {
pkt.Header, err = ParseRtpHeader(b)
if err != nil {
return
}
pkt.Raw = make([]byte, len(b))
copy(pkt.Raw, b)
return
}
func (p *RtpPacket) Body() []byte {
if p.Header.payloadOffset == 0 {
Log.Warnf("CHEFNOTICEME. payloadOffset=%d", p.Header.payloadOffset)
p.Header.payloadOffset = RtpFixedHeaderLength
}
if p.Header.Padding == 1 {
return p.Raw[p.Header.payloadOffset : len(p.Raw)-p.Header.paddingLength]
}
return p.Raw[p.Header.payloadOffset:]
}
// IsAvcHevcBoundary
//
// @param pt: 取值范围为AvPacketPtAvc或AvPacketPtHevc否则直接返回false
func IsAvcHevcBoundary(pkt RtpPacket, pt base.AvPacketPt) bool {
switch pt {
case base.AvPacketPtAvc:
return IsAvcBoundary(pkt)
case base.AvPacketPtHevc:
return IsHevcBoundary(pkt)
}
return false
}
func IsAvcBoundary(pkt RtpPacket) bool {
boundaryNaluTypes := map[uint8]struct{}{
avc.NaluTypeSps: {},
avc.NaluTypePps: {},
avc.NaluTypeIdrSlice: {},
}
// TODO(chef): [fix] 检查数据长度有效性 202211
b := pkt.Body()
outerNaluType := avc.ParseNaluType(b[0])
if _, ok := boundaryNaluTypes[outerNaluType]; ok {
return true
}
if outerNaluType == NaluTypeAvcStapa {
t := avc.ParseNaluType(b[3])
if _, ok := boundaryNaluTypes[t]; ok {
return true
}
}
if outerNaluType == NaluTypeAvcFua {
t := avc.ParseNaluType(b[1])
if _, ok := boundaryNaluTypes[t]; ok {
if b[1]&0x80 != 0 {
return true
}
}
}
return false
}
func IsHevcBoundary(pkt RtpPacket) bool {
boundaryNaluTypes := map[uint8]struct{}{
hevc.NaluTypeVps: {},
hevc.NaluTypeSps: {},
hevc.NaluTypePps: {},
hevc.NaluTypeSliceBlaWlp: {},
hevc.NaluTypeSliceBlaWradl: {},
hevc.NaluTypeSliceBlaNlp: {},
hevc.NaluTypeSliceIdr: {},
hevc.NaluTypeSliceIdrNlp: {},
hevc.NaluTypeSliceCranut: {},
hevc.NaluTypeSliceRsvIrapVcl22: {},
hevc.NaluTypeSliceRsvIrapVcl23: {},
}
// TODO(chef): [fix] 检查数据长度有效性 202211
b := pkt.Body()
outerNaluType := hevc.ParseNaluType(b[0])
if _, ok := boundaryNaluTypes[outerNaluType]; ok {
return true
}
if outerNaluType == NaluTypeHevcFua {
t := b[2] & 0x3F // 注意这里是后6位不是中间6位
if _, ok := boundaryNaluTypes[t]; ok {
if b[2]&0x80 != 0 {
return true
}
}
}
return false
}