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

267 lines
6.6 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 |
// | .... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// TODO(chef): ext 202210
// TODO(chef): padding 202210
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
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
bele.BeUint16(b[offset:])
offset += 2
extensionLength := bele.BeUint16(b[offset:])
offset += 2
offset += int(extensionLength)
// TODO(chef): [feat] 当前只是跳过了extension后续考虑解析具体的extension内容存储至结构体中 202211
}
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
}