直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
297
server/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
297
server/vendor/github.com/pion/rtp/codecs/h264_packet.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// H264Payloader payloads H264 packets
|
||||
type H264Payloader struct {
|
||||
spsNalu, ppsNalu []byte
|
||||
}
|
||||
|
||||
const (
|
||||
stapaNALUType = 24
|
||||
fuaNALUType = 28
|
||||
fubNALUType = 29
|
||||
spsNALUType = 7
|
||||
ppsNALUType = 8
|
||||
audNALUType = 9
|
||||
fillerNALUType = 12
|
||||
|
||||
fuaHeaderSize = 2
|
||||
stapaHeaderSize = 1
|
||||
stapaNALULengthSize = 2
|
||||
|
||||
naluTypeBitmask = 0x1F
|
||||
naluRefIdcBitmask = 0x60
|
||||
fuStartBitmask = 0x80
|
||||
fuEndBitmask = 0x40
|
||||
|
||||
outputStapAHeader = 0x78
|
||||
)
|
||||
|
||||
// nolint:gochecknoglobals
|
||||
var (
|
||||
naluStartCode = []byte{0x00, 0x00, 0x01}
|
||||
annexbNALUStartCode = []byte{0x00, 0x00, 0x00, 0x01}
|
||||
)
|
||||
|
||||
func emitNalus(nals []byte, emit func([]byte)) {
|
||||
start := 0
|
||||
length := len(nals)
|
||||
|
||||
for start < length {
|
||||
end := bytes.Index(nals[start:], annexbNALUStartCode)
|
||||
offset := 4
|
||||
if end == -1 {
|
||||
end = bytes.Index(nals[start:], naluStartCode)
|
||||
offset = 3
|
||||
}
|
||||
if end == -1 {
|
||||
emit(nals[start:])
|
||||
break
|
||||
}
|
||||
|
||||
emit(nals[start : start+end])
|
||||
|
||||
// next NAL start position
|
||||
start += end + offset
|
||||
}
|
||||
}
|
||||
|
||||
// Payload fragments a H264 packet across one or more byte arrays
|
||||
func (p *H264Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var payloads [][]byte
|
||||
if len(payload) == 0 {
|
||||
return payloads
|
||||
}
|
||||
|
||||
emitNalus(payload, func(nalu []byte) {
|
||||
if len(nalu) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
naluType := nalu[0] & naluTypeBitmask
|
||||
naluRefIdc := nalu[0] & naluRefIdcBitmask
|
||||
|
||||
switch {
|
||||
case naluType == audNALUType || naluType == fillerNALUType:
|
||||
return
|
||||
case naluType == spsNALUType:
|
||||
p.spsNalu = nalu
|
||||
return
|
||||
case naluType == ppsNALUType:
|
||||
p.ppsNalu = nalu
|
||||
return
|
||||
case p.spsNalu != nil && p.ppsNalu != nil:
|
||||
// Pack current NALU with SPS and PPS as STAP-A
|
||||
spsLen := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(spsLen, uint16(len(p.spsNalu)))
|
||||
|
||||
ppsLen := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(ppsLen, uint16(len(p.ppsNalu)))
|
||||
|
||||
stapANalu := []byte{outputStapAHeader}
|
||||
stapANalu = append(stapANalu, spsLen...)
|
||||
stapANalu = append(stapANalu, p.spsNalu...)
|
||||
stapANalu = append(stapANalu, ppsLen...)
|
||||
stapANalu = append(stapANalu, p.ppsNalu...)
|
||||
if len(stapANalu) <= int(mtu) {
|
||||
out := make([]byte, len(stapANalu))
|
||||
copy(out, stapANalu)
|
||||
payloads = append(payloads, out)
|
||||
}
|
||||
|
||||
p.spsNalu = nil
|
||||
p.ppsNalu = nil
|
||||
}
|
||||
|
||||
// Single NALU
|
||||
if len(nalu) <= int(mtu) {
|
||||
out := make([]byte, len(nalu))
|
||||
copy(out, nalu)
|
||||
payloads = append(payloads, out)
|
||||
return
|
||||
}
|
||||
|
||||
// FU-A
|
||||
maxFragmentSize := int(mtu) - fuaHeaderSize
|
||||
|
||||
// The FU payload consists of fragments of the payload of the fragmented
|
||||
// NAL unit so that if the fragmentation unit payloads of consecutive
|
||||
// FUs are sequentially concatenated, the payload of the fragmented NAL
|
||||
// unit can be reconstructed. The NAL unit type octet of the fragmented
|
||||
// NAL unit is not included as such in the fragmentation unit payload,
|
||||
// but rather the information of the NAL unit type octet of the
|
||||
// fragmented NAL unit is conveyed in the F and NRI fields of the FU
|
||||
// indicator octet of the fragmentation unit and in the type field of
|
||||
// the FU header. An FU payload MAY have any number of octets and MAY
|
||||
// be empty.
|
||||
|
||||
// According to the RFC, the first octet is skipped due to redundant information
|
||||
naluIndex := 1
|
||||
naluLength := len(nalu) - naluIndex
|
||||
naluRemaining := naluLength
|
||||
|
||||
if min(maxFragmentSize, naluRemaining) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for naluRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, naluRemaining)
|
||||
out := make([]byte, fuaHeaderSize+currentFragmentSize)
|
||||
|
||||
// +---------------+
|
||||
// |0|1|2|3|4|5|6|7|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |F|NRI| Type |
|
||||
// +---------------+
|
||||
out[0] = fuaNALUType
|
||||
out[0] |= naluRefIdc
|
||||
|
||||
// +---------------+
|
||||
// |0|1|2|3|4|5|6|7|
|
||||
// +-+-+-+-+-+-+-+-+
|
||||
// |S|E|R| Type |
|
||||
// +---------------+
|
||||
|
||||
out[1] = naluType
|
||||
if naluRemaining == naluLength {
|
||||
// Set start bit
|
||||
out[1] |= 1 << 7
|
||||
} else if naluRemaining-currentFragmentSize == 0 {
|
||||
// Set end bit
|
||||
out[1] |= 1 << 6
|
||||
}
|
||||
|
||||
copy(out[fuaHeaderSize:], nalu[naluIndex:naluIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
naluRemaining -= currentFragmentSize
|
||||
naluIndex += currentFragmentSize
|
||||
}
|
||||
})
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// H264Packet represents the H264 header that is stored in the payload of an RTP Packet
|
||||
type H264Packet struct {
|
||||
IsAVC bool
|
||||
fuaBuffer []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
func (p *H264Packet) doPackaging(buf, nalu []byte) []byte {
|
||||
if p.IsAVC {
|
||||
buf = binary.BigEndian.AppendUint32(buf, uint32(len(nalu)))
|
||||
buf = append(buf, nalu...)
|
||||
return buf
|
||||
}
|
||||
|
||||
buf = append(buf, annexbNALUStartCode...)
|
||||
buf = append(buf, nalu...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// IsDetectedFinalPacketInSequence returns true of the packet passed in has the
|
||||
// marker bit set indicated the end of a packet sequence
|
||||
func (p *H264Packet) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
|
||||
return rtpPacketMarketBit
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
|
||||
func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if p.zeroAllocation {
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
return p.parseBody(payload)
|
||||
}
|
||||
|
||||
func (p *H264Packet) parseBody(payload []byte) ([]byte, error) {
|
||||
if len(payload) == 0 {
|
||||
return nil, fmt.Errorf("%w: %d <=0", errShortPacket, len(payload))
|
||||
}
|
||||
|
||||
// NALU Types
|
||||
// https://tools.ietf.org/html/rfc6184#section-5.4
|
||||
naluType := payload[0] & naluTypeBitmask
|
||||
switch {
|
||||
case naluType > 0 && naluType < 24:
|
||||
return p.doPackaging(nil, payload), nil
|
||||
|
||||
case naluType == stapaNALUType:
|
||||
currOffset := int(stapaHeaderSize)
|
||||
result := []byte{}
|
||||
for currOffset < len(payload) {
|
||||
naluSize := int(binary.BigEndian.Uint16(payload[currOffset:]))
|
||||
currOffset += stapaNALULengthSize
|
||||
|
||||
if len(payload) < currOffset+naluSize {
|
||||
return nil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)", errShortPacket, naluSize, len(payload)-currOffset)
|
||||
}
|
||||
|
||||
result = p.doPackaging(result, payload[currOffset:currOffset+naluSize])
|
||||
currOffset += naluSize
|
||||
}
|
||||
return result, nil
|
||||
|
||||
case naluType == fuaNALUType:
|
||||
if len(payload) < fuaHeaderSize {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
if p.fuaBuffer == nil {
|
||||
p.fuaBuffer = []byte{}
|
||||
}
|
||||
|
||||
p.fuaBuffer = append(p.fuaBuffer, payload[fuaHeaderSize:]...)
|
||||
|
||||
if payload[1]&fuEndBitmask != 0 {
|
||||
naluRefIdc := payload[0] & naluRefIdcBitmask
|
||||
fragmentedNaluType := payload[1] & naluTypeBitmask
|
||||
|
||||
nalu := append([]byte{}, naluRefIdc|fragmentedNaluType)
|
||||
nalu = append(nalu, p.fuaBuffer...)
|
||||
p.fuaBuffer = nil
|
||||
return p.doPackaging(nil, nalu), nil
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
|
||||
}
|
||||
|
||||
// H264PartitionHeadChecker checks H264 partition head.
|
||||
//
|
||||
// Deprecated: replaced by H264Packet.IsPartitionHead()
|
||||
type H264PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
//
|
||||
// Deprecated: replaced by H264Packet.IsPartitionHead()
|
||||
func (*H264PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&H264Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
func (*H264Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if payload[0]&naluTypeBitmask == fuaNALUType ||
|
||||
payload[0]&naluTypeBitmask == fubNALUType {
|
||||
return payload[1]&fuStartBitmask != 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user