直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理
Made-with: Cursor
This commit is contained in:
85
server/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
generated
vendored
Normal file
85
server/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package obu implements tools for working with the Open Bitstream Unit.
|
||||
package obu
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
sevenLsbBitmask = uint(0b01111111)
|
||||
msbBitmask = uint(0b10000000)
|
||||
)
|
||||
|
||||
// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read
|
||||
var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished")
|
||||
|
||||
// EncodeLEB128 encodes a uint as LEB128
|
||||
func EncodeLEB128(in uint) (out uint) {
|
||||
for {
|
||||
// Copy seven bits from in and discard
|
||||
// what we have copied from in
|
||||
out |= (in & sevenLsbBitmask)
|
||||
in >>= 7
|
||||
|
||||
// If we have more bits to encode set MSB
|
||||
// otherwise we are done
|
||||
if in != 0 {
|
||||
out |= msbBitmask
|
||||
out <<= 8
|
||||
} else {
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeLEB128(in uint) (out uint) {
|
||||
for {
|
||||
// Take 7 LSB from in
|
||||
out |= (in & sevenLsbBitmask)
|
||||
|
||||
// Discard the MSB
|
||||
in >>= 8
|
||||
if in == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
out <<= 7
|
||||
}
|
||||
}
|
||||
|
||||
// ReadLeb128 scans an buffer and decodes a Leb128 value.
|
||||
// If the end of the buffer is reached and all MSB are set
|
||||
// an error is returned
|
||||
func ReadLeb128(in []byte) (uint, uint, error) {
|
||||
var encodedLength uint
|
||||
|
||||
for i := range in {
|
||||
encodedLength |= uint(in[i])
|
||||
|
||||
if in[i]&byte(msbBitmask) == 0 {
|
||||
return decodeLEB128(encodedLength), uint(i + 1), nil
|
||||
}
|
||||
|
||||
// Make more room for next read
|
||||
encodedLength <<= 8
|
||||
}
|
||||
|
||||
return 0, 0, ErrFailedToReadLEB128
|
||||
}
|
||||
|
||||
// WriteToLeb128 writes a uint to a LEB128 encoded byte slice.
|
||||
func WriteToLeb128(in uint) []byte {
|
||||
b := make([]byte, 10)
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
b[i] = byte(in & 0x7f)
|
||||
in >>= 7
|
||||
if in == 0 {
|
||||
return b[:i+1]
|
||||
}
|
||||
b[i] |= 0x80
|
||||
}
|
||||
|
||||
return b // unreachable
|
||||
}
|
||||
204
server/vendor/github.com/pion/rtp/codecs/av1_packet.go
generated
vendored
Normal file
204
server/vendor/github.com/pion/rtp/codecs/av1_packet.go
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"github.com/pion/rtp/codecs/av1/obu"
|
||||
)
|
||||
|
||||
const (
|
||||
zMask = byte(0b10000000)
|
||||
zBitshift = 7
|
||||
|
||||
yMask = byte(0b01000000)
|
||||
yBitshift = 6
|
||||
|
||||
wMask = byte(0b00110000)
|
||||
wBitshift = 4
|
||||
|
||||
nMask = byte(0b00001000)
|
||||
nBitshift = 3
|
||||
|
||||
obuFrameTypeMask = byte(0b01111000)
|
||||
obuFrameTypeBitshift = 3
|
||||
|
||||
obuFameTypeSequenceHeader = 1
|
||||
|
||||
av1PayloaderHeadersize = 1
|
||||
|
||||
leb128Size = 1
|
||||
)
|
||||
|
||||
// AV1Payloader payloads AV1 packets
|
||||
type AV1Payloader struct {
|
||||
sequenceHeader []byte
|
||||
}
|
||||
|
||||
// Payload fragments a AV1 packet across one or more byte arrays
|
||||
// See AV1Packet for description of AV1 Payload Header
|
||||
func (p *AV1Payloader) Payload(mtu uint16, payload []byte) (payloads [][]byte) {
|
||||
payloadDataIndex := 0
|
||||
payloadDataRemaining := len(payload)
|
||||
|
||||
// Payload Data and MTU is non-zero
|
||||
if mtu <= 0 || payloadDataRemaining <= 0 {
|
||||
return payloads
|
||||
}
|
||||
|
||||
// Cache Sequence Header and packetize with next payload
|
||||
frameType := (payload[0] & obuFrameTypeMask) >> obuFrameTypeBitshift
|
||||
if frameType == obuFameTypeSequenceHeader {
|
||||
p.sequenceHeader = payload
|
||||
return
|
||||
}
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
obuCount := byte(1)
|
||||
metadataSize := av1PayloaderHeadersize
|
||||
if len(p.sequenceHeader) != 0 {
|
||||
obuCount++
|
||||
metadataSize += leb128Size + len(p.sequenceHeader)
|
||||
}
|
||||
|
||||
out := make([]byte, min(int(mtu), payloadDataRemaining+metadataSize))
|
||||
outOffset := av1PayloaderHeadersize
|
||||
out[0] = obuCount << wBitshift
|
||||
|
||||
if obuCount == 2 {
|
||||
// This Payload contain the start of a Coded Video Sequence
|
||||
out[0] ^= nMask
|
||||
|
||||
out[1] = byte(obu.EncodeLEB128(uint(len(p.sequenceHeader))))
|
||||
copy(out[2:], p.sequenceHeader)
|
||||
|
||||
outOffset += leb128Size + len(p.sequenceHeader)
|
||||
|
||||
p.sequenceHeader = nil
|
||||
}
|
||||
|
||||
outBufferRemaining := len(out) - outOffset
|
||||
copy(out[outOffset:], payload[payloadDataIndex:payloadDataIndex+outBufferRemaining])
|
||||
payloadDataRemaining -= outBufferRemaining
|
||||
payloadDataIndex += outBufferRemaining
|
||||
|
||||
// Does this Fragment contain an OBU that started in a previous payload
|
||||
if len(payloads) > 0 {
|
||||
out[0] ^= zMask
|
||||
}
|
||||
|
||||
// This OBU will be continued in next Payload
|
||||
if payloadDataRemaining != 0 {
|
||||
out[0] ^= yMask
|
||||
}
|
||||
|
||||
payloads = append(payloads, out)
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// AV1Packet represents a depacketized AV1 RTP Packet
|
||||
/*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |Z|Y| W |N|-|-|-|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header
|
||||
type AV1Packet struct {
|
||||
// Z: MUST be set to 1 if the first OBU element is an
|
||||
// OBU fragment that is a continuation of an OBU fragment
|
||||
// from the previous packet, and MUST be set to 0 otherwise.
|
||||
Z bool
|
||||
|
||||
// Y: MUST be set to 1 if the last OBU element is an OBU fragment
|
||||
// that will continue in the next packet, and MUST be set to 0 otherwise.
|
||||
Y bool
|
||||
|
||||
// W: two bit field that describes the number of OBU elements in the packet.
|
||||
// This field MUST be set equal to 0 or equal to the number of OBU elements
|
||||
// contained in the packet. If set to 0, each OBU element MUST be preceded by
|
||||
// a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element
|
||||
// MUST NOT be preceded by a length field. Instead, the length of the last OBU
|
||||
// element contained in the packet can be calculated as follows:
|
||||
// Length of the last OBU element =
|
||||
// length of the RTP payload
|
||||
// - length of aggregation header
|
||||
// - length of previous OBU elements including length fields
|
||||
W byte
|
||||
|
||||
// N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set to 0 otherwise.
|
||||
N bool
|
||||
|
||||
// Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one.
|
||||
// AV1Frame provides the tools to construct a collection of OBUs from a collection of OBU Elements
|
||||
OBUElements [][]byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon
|
||||
func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.Z = ((payload[0] & zMask) >> zBitshift) != 0
|
||||
p.Y = ((payload[0] & yMask) >> yBitshift) != 0
|
||||
p.N = ((payload[0] & nMask) >> nBitshift) != 0
|
||||
p.W = (payload[0] & wMask) >> wBitshift
|
||||
|
||||
if p.Z && p.N {
|
||||
return nil, errIsKeyframeAndFragment
|
||||
}
|
||||
|
||||
if !p.zeroAllocation {
|
||||
obuElements, err := p.parseBody(payload[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.OBUElements = obuElements
|
||||
}
|
||||
|
||||
return payload[1:], nil
|
||||
}
|
||||
|
||||
func (p *AV1Packet) parseBody(payload []byte) ([][]byte, error) {
|
||||
if p.OBUElements != nil {
|
||||
return p.OBUElements, nil
|
||||
}
|
||||
|
||||
obuElements := [][]byte{}
|
||||
|
||||
var obuElementLength, bytesRead uint
|
||||
currentIndex := uint(0)
|
||||
for i := 1; ; i++ {
|
||||
if currentIndex == uint(len(payload)) {
|
||||
break
|
||||
}
|
||||
|
||||
// If W bit is set the last OBU Element will have no length header
|
||||
if byte(i) == p.W {
|
||||
bytesRead = 0
|
||||
obuElementLength = uint(len(payload)) - currentIndex
|
||||
} else {
|
||||
var err error
|
||||
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex += bytesRead
|
||||
if uint(len(payload)) < currentIndex+obuElementLength {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
obuElements = append(obuElements, payload[currentIndex:currentIndex+obuElementLength])
|
||||
currentIndex += obuElementLength
|
||||
}
|
||||
|
||||
return obuElements, nil
|
||||
}
|
||||
5
server/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
5
server/vendor/github.com/pion/rtp/codecs/codecs.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package codecs implements codec specific RTP payloader/depayloaders
|
||||
package codecs
|
||||
39
server/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
39
server/vendor/github.com/pion/rtp/codecs/common.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// audioDepacketizer is a mixin for audio codec depacketizers
|
||||
type audioDepacketizer struct{}
|
||||
|
||||
func (d *audioDepacketizer) IsPartitionTail(_ bool, _ []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *audioDepacketizer) IsPartitionHead(_ []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// videoDepacketizer is a mixin for video codec depacketizers
|
||||
type videoDepacketizer struct {
|
||||
zeroAllocation bool
|
||||
}
|
||||
|
||||
func (d *videoDepacketizer) IsPartitionTail(marker bool, _ []byte) bool {
|
||||
return marker
|
||||
}
|
||||
|
||||
// SetZeroAllocation enables Zero Allocation mode for the depacketizer
|
||||
// By default the Depacketizers will allocate as they parse. These allocations
|
||||
// are needed for Metadata and other optional values. If you don't need this information
|
||||
// enabling SetZeroAllocation gives you higher performance at a reduced feature set.
|
||||
func (d *videoDepacketizer) SetZeroAllocation(zeroAllocation bool) {
|
||||
d.zeroAllocation = zeroAllocation
|
||||
}
|
||||
17
server/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
17
server/vendor/github.com/pion/rtp/codecs/error.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
errShortPacket = errors.New("packet is not large enough")
|
||||
errNilPacket = errors.New("invalid nil packet")
|
||||
errTooManyPDiff = errors.New("too many PDiff")
|
||||
errTooManySpatialLayers = errors.New("too many spatial layers")
|
||||
errUnhandledNALUType = errors.New("NALU Type is unhandled")
|
||||
|
||||
// AV1 Errors
|
||||
errIsKeyframeAndFragment = errors.New("bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe")
|
||||
)
|
||||
25
server/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
25
server/vendor/github.com/pion/rtp/codecs/g711_packet.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// G711Payloader payloads G711 packets
|
||||
type G711Payloader struct{}
|
||||
|
||||
// Payload fragments an G711 packet across one or more byte arrays
|
||||
func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var out [][]byte
|
||||
if payload == nil || mtu == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
for len(payload) > int(mtu) {
|
||||
o := make([]byte, mtu)
|
||||
copy(o, payload[:mtu])
|
||||
payload = payload[mtu:]
|
||||
out = append(out, o)
|
||||
}
|
||||
o := make([]byte, len(payload))
|
||||
copy(o, payload)
|
||||
return append(out, o)
|
||||
}
|
||||
25
server/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
25
server/vendor/github.com/pion/rtp/codecs/g722_packet.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// G722Payloader payloads G722 packets
|
||||
type G722Payloader struct{}
|
||||
|
||||
// Payload fragments an G722 packet across one or more byte arrays
|
||||
func (p *G722Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
var out [][]byte
|
||||
if payload == nil || mtu == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
for len(payload) > int(mtu) {
|
||||
o := make([]byte, mtu)
|
||||
copy(o, payload[:mtu])
|
||||
payload = payload[mtu:]
|
||||
out = append(out, o)
|
||||
}
|
||||
o := make([]byte, len(payload))
|
||||
copy(o, payload)
|
||||
return append(out, o)
|
||||
}
|
||||
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
|
||||
}
|
||||
828
server/vendor/github.com/pion/rtp/codecs/h265_packet.go
generated
vendored
Normal file
828
server/vendor/github.com/pion/rtp/codecs/h265_packet.go
generated
vendored
Normal file
@@ -0,0 +1,828 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//
|
||||
// Errors
|
||||
//
|
||||
|
||||
var (
|
||||
errH265CorruptedPacket = errors.New("corrupted h265 packet")
|
||||
errInvalidH265PacketType = errors.New("invalid h265 packet type")
|
||||
)
|
||||
|
||||
//
|
||||
// Network Abstraction Unit Header implementation
|
||||
//
|
||||
|
||||
const (
|
||||
// sizeof(uint16)
|
||||
h265NaluHeaderSize = 2
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
h265NaluAggregationPacketType = 48
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
|
||||
h265NaluFragmentationUnitType = 49
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
|
||||
h265NaluPACIPacketType = 50
|
||||
)
|
||||
|
||||
// H265NALUHeader is a H265 NAL Unit Header
|
||||
// https://datatracker.ietf.org/doc/html/rfc7798#section-1.1.4
|
||||
/*
|
||||
* +---------------+---------------+
|
||||
* |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |F| Type | LayerID | TID |
|
||||
* +-------------+-----------------+
|
||||
**/
|
||||
type H265NALUHeader uint16
|
||||
|
||||
func newH265NALUHeader(highByte, lowByte uint8) H265NALUHeader {
|
||||
return H265NALUHeader((uint16(highByte) << 8) | uint16(lowByte))
|
||||
}
|
||||
|
||||
// F is the forbidden bit, should always be 0.
|
||||
func (h H265NALUHeader) F() bool {
|
||||
return (uint16(h) >> 15) != 0
|
||||
}
|
||||
|
||||
// Type of NAL Unit.
|
||||
func (h H265NALUHeader) Type() uint8 {
|
||||
// 01111110 00000000
|
||||
const mask = 0b01111110 << 8
|
||||
return uint8((uint16(h) & mask) >> (8 + 1))
|
||||
}
|
||||
|
||||
// IsTypeVCLUnit returns whether or not the NAL Unit type is a VCL NAL unit.
|
||||
func (h H265NALUHeader) IsTypeVCLUnit() bool {
|
||||
// Type is coded on 6 bits
|
||||
const msbMask = 0b00100000
|
||||
return (h.Type() & msbMask) == 0
|
||||
}
|
||||
|
||||
// LayerID should always be 0 in non-3D HEVC context.
|
||||
func (h H265NALUHeader) LayerID() uint8 {
|
||||
// 00000001 11111000
|
||||
const mask = (0b00000001 << 8) | 0b11111000
|
||||
return uint8((uint16(h) & mask) >> 3)
|
||||
}
|
||||
|
||||
// TID is the temporal identifier of the NAL unit +1.
|
||||
func (h H265NALUHeader) TID() uint8 {
|
||||
const mask = 0b00000111
|
||||
return uint8(uint16(h) & mask)
|
||||
}
|
||||
|
||||
// IsAggregationPacket returns whether or not the packet is an Aggregation packet.
|
||||
func (h H265NALUHeader) IsAggregationPacket() bool {
|
||||
return h.Type() == h265NaluAggregationPacketType
|
||||
}
|
||||
|
||||
// IsFragmentationUnit returns whether or not the packet is a Fragmentation Unit packet.
|
||||
func (h H265NALUHeader) IsFragmentationUnit() bool {
|
||||
return h.Type() == h265NaluFragmentationUnitType
|
||||
}
|
||||
|
||||
// IsPACIPacket returns whether or not the packet is a PACI packet.
|
||||
func (h H265NALUHeader) IsPACIPacket() bool {
|
||||
return h.Type() == h265NaluPACIPacketType
|
||||
}
|
||||
|
||||
//
|
||||
// Single NAL Unit Packet implementation
|
||||
//
|
||||
|
||||
// H265SingleNALUnitPacket represents a NALU packet, containing exactly one NAL unit.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr | DONL (conditional) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | NAL unit payload data |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.1
|
||||
type H265SingleNALUnitPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
// donl is a 16-bit field, that may or may not be present.
|
||||
donl *uint16
|
||||
// payload of the fragmentation unit.
|
||||
payload []byte
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265SingleNALUnitPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265SingleNALUnitPacket this method is called upon.
|
||||
func (p *H265SingleNALUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if payloadHeader.IsFragmentationUnit() || payloadHeader.IsPACIPacket() || payloadHeader.IsAggregationPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
payload = payload[2:]
|
||||
|
||||
if p.mightNeedDONL {
|
||||
// sizeof(uint16)
|
||||
if len(payload) <= 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
p.donl = &donl
|
||||
payload = payload[2:]
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NALU header of the packet.
|
||||
func (p *H265SingleNALUnitPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// DONL returns the DONL of the packet.
|
||||
func (p *H265SingleNALUnitPacket) DONL() *uint16 {
|
||||
return p.donl
|
||||
}
|
||||
|
||||
// Payload returns the Fragmentation Unit packet payload.
|
||||
func (p *H265SingleNALUnitPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
func (p *H265SingleNALUnitPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Aggregation Packets implementation
|
||||
//
|
||||
|
||||
// H265AggregationUnitFirst represent the First Aggregation Unit in an AP.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* : DONL (conditional) | NALU size |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | NALU size | |
|
||||
* +-+-+-+-+-+-+-+-+ NAL unit |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationUnitFirst struct {
|
||||
donl *uint16
|
||||
nalUnitSize uint16
|
||||
nalUnit []byte
|
||||
}
|
||||
|
||||
// DONL field, when present, specifies the value of the 16 least
|
||||
// significant bits of the decoding order number of the aggregated NAL
|
||||
// unit.
|
||||
func (u H265AggregationUnitFirst) DONL() *uint16 {
|
||||
return u.donl
|
||||
}
|
||||
|
||||
// NALUSize represents the size, in bytes, of the NalUnit.
|
||||
func (u H265AggregationUnitFirst) NALUSize() uint16 {
|
||||
return u.nalUnitSize
|
||||
}
|
||||
|
||||
// NalUnit payload.
|
||||
func (u H265AggregationUnitFirst) NalUnit() []byte {
|
||||
return u.nalUnit
|
||||
}
|
||||
|
||||
// H265AggregationUnit represent the an Aggregation Unit in an AP, which is not the first one.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* : DOND (cond) | NALU size |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | NAL unit |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationUnit struct {
|
||||
dond *uint8
|
||||
nalUnitSize uint16
|
||||
nalUnit []byte
|
||||
}
|
||||
|
||||
// DOND field plus 1 specifies the difference between
|
||||
// the decoding order number values of the current aggregated NAL unit
|
||||
// and the preceding aggregated NAL unit in the same AP.
|
||||
func (u H265AggregationUnit) DOND() *uint8 {
|
||||
return u.dond
|
||||
}
|
||||
|
||||
// NALUSize represents the size, in bytes, of the NalUnit.
|
||||
func (u H265AggregationUnit) NALUSize() uint16 {
|
||||
return u.nalUnitSize
|
||||
}
|
||||
|
||||
// NalUnit payload.
|
||||
func (u H265AggregationUnit) NalUnit() []byte {
|
||||
return u.nalUnit
|
||||
}
|
||||
|
||||
// H265AggregationPacket represents an Aggregation packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=48) | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
||||
* | |
|
||||
* | two or more aggregation units |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.2
|
||||
type H265AggregationPacket struct {
|
||||
firstUnit *H265AggregationUnitFirst
|
||||
otherUnits []H265AggregationUnit
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265AggregationPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265AggregationPacket this method is called upon.
|
||||
func (p *H265AggregationPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsAggregationPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
// First parse the first aggregation unit
|
||||
payload = payload[2:]
|
||||
firstUnit := &H265AggregationUnitFirst{}
|
||||
|
||||
if p.mightNeedDONL {
|
||||
if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
firstUnit.donl = &donl
|
||||
|
||||
payload = payload[2:]
|
||||
}
|
||||
if len(payload) < 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
firstUnit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if len(payload) < int(firstUnit.nalUnitSize) {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
firstUnit.nalUnit = payload[:firstUnit.nalUnitSize]
|
||||
payload = payload[firstUnit.nalUnitSize:]
|
||||
|
||||
// Parse remaining Aggregation Units
|
||||
var units []H265AggregationUnit
|
||||
for {
|
||||
unit := H265AggregationUnit{}
|
||||
|
||||
if p.mightNeedDONL {
|
||||
if len(payload) < 1 {
|
||||
break
|
||||
}
|
||||
|
||||
dond := payload[0]
|
||||
unit.dond = &dond
|
||||
|
||||
payload = payload[1:]
|
||||
}
|
||||
|
||||
if len(payload) < 2 {
|
||||
break
|
||||
}
|
||||
unit.nalUnitSize = (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
payload = payload[2:]
|
||||
|
||||
if len(payload) < int(unit.nalUnitSize) {
|
||||
break
|
||||
}
|
||||
|
||||
unit.nalUnit = payload[:unit.nalUnitSize]
|
||||
payload = payload[unit.nalUnitSize:]
|
||||
|
||||
units = append(units, unit)
|
||||
}
|
||||
|
||||
// There need to be **at least** two Aggregation Units (first + another one)
|
||||
if len(units) == 0 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.firstUnit = firstUnit
|
||||
p.otherUnits = units
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// FirstUnit returns the first Aggregated Unit of the packet.
|
||||
func (p *H265AggregationPacket) FirstUnit() *H265AggregationUnitFirst {
|
||||
return p.firstUnit
|
||||
}
|
||||
|
||||
// OtherUnits returns the all the other Aggregated Unit of the packet (excluding the first one).
|
||||
func (p *H265AggregationPacket) OtherUnits() []H265AggregationUnit {
|
||||
return p.otherUnits
|
||||
}
|
||||
|
||||
func (p *H265AggregationPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Fragmentation Unit implementation
|
||||
//
|
||||
|
||||
const (
|
||||
// sizeof(uint8)
|
||||
h265FragmentationUnitHeaderSize = 1
|
||||
)
|
||||
|
||||
// H265FragmentationUnitHeader is a H265 FU Header
|
||||
/*
|
||||
* +---------------+
|
||||
* |0|1|2|3|4|5|6|7|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |S|E| FuType |
|
||||
* +---------------+
|
||||
**/
|
||||
type H265FragmentationUnitHeader uint8
|
||||
|
||||
// S represents the start of a fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) S() bool {
|
||||
const mask = 0b10000000
|
||||
return ((h & mask) >> 7) != 0
|
||||
}
|
||||
|
||||
// E represents the end of a fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) E() bool {
|
||||
const mask = 0b01000000
|
||||
return ((h & mask) >> 6) != 0
|
||||
}
|
||||
|
||||
// FuType MUST be equal to the field Type of the fragmented NAL unit.
|
||||
func (h H265FragmentationUnitHeader) FuType() uint8 {
|
||||
const mask = 0b00111111
|
||||
return uint8(h) & mask
|
||||
}
|
||||
|
||||
// H265FragmentationUnitPacket represents a single Fragmentation Unit packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=49) | FU header | DONL (cond) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
|
||||
* | DONL (cond) | |
|
||||
* |-+-+-+-+-+-+-+-+ |
|
||||
* | FU payload |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.3
|
||||
type H265FragmentationUnitPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
// fuHeader is the header of the fragmentation unit
|
||||
fuHeader H265FragmentationUnitHeader
|
||||
// donl is a 16-bit field, that may or may not be present.
|
||||
donl *uint16
|
||||
// payload of the fragmentation unit.
|
||||
payload []byte
|
||||
|
||||
mightNeedDONL bool
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265FragmentationUnitPacket) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265FragmentationUnitPacket this method is called upon.
|
||||
func (p *H265FragmentationUnitPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize + h265FragmentationUnitHeaderSize
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsFragmentationUnit() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
fuHeader := H265FragmentationUnitHeader(payload[2])
|
||||
payload = payload[3:]
|
||||
|
||||
if fuHeader.S() && p.mightNeedDONL {
|
||||
// sizeof(uint16)
|
||||
if len(payload) <= 2 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
donl := (uint16(payload[0]) << 8) | uint16(payload[1])
|
||||
p.donl = &donl
|
||||
payload = payload[2:]
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
p.fuHeader = fuHeader
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NALU header of the packet.
|
||||
func (p *H265FragmentationUnitPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// FuHeader returns the Fragmentation Unit Header of the packet.
|
||||
func (p *H265FragmentationUnitPacket) FuHeader() H265FragmentationUnitHeader {
|
||||
return p.fuHeader
|
||||
}
|
||||
|
||||
// DONL returns the DONL of the packet.
|
||||
func (p *H265FragmentationUnitPacket) DONL() *uint16 {
|
||||
return p.donl
|
||||
}
|
||||
|
||||
// Payload returns the Fragmentation Unit packet payload.
|
||||
func (p *H265FragmentationUnitPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
func (p *H265FragmentationUnitPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// PACI implementation
|
||||
//
|
||||
|
||||
// H265PACIPacket represents a single H265 PACI packet.
|
||||
/*
|
||||
* 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
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | PayloadHdr (Type=50) |A| cType | PHSsize |F0..2|Y|
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Payload Header Extension Structure (PHES) |
|
||||
* |=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=|
|
||||
* | |
|
||||
* | PACI payload: NAL unit |
|
||||
* | . . . |
|
||||
* | |
|
||||
* | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | :...OPTIONAL RTP padding |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.4.4
|
||||
type H265PACIPacket struct {
|
||||
// payloadHeader is the header of the H265 packet.
|
||||
payloadHeader H265NALUHeader
|
||||
|
||||
// Field which holds value for `A`, `cType`, `PHSsize`, `F0`, `F1`, `F2` and `Y` fields.
|
||||
paciHeaderFields uint16
|
||||
|
||||
// phes is a header extension, of byte length `PHSsize`
|
||||
phes []byte
|
||||
|
||||
// Payload contains NAL units & optional padding
|
||||
payload []byte
|
||||
}
|
||||
|
||||
// PayloadHeader returns the NAL Unit Header.
|
||||
func (p *H265PACIPacket) PayloadHeader() H265NALUHeader {
|
||||
return p.payloadHeader
|
||||
}
|
||||
|
||||
// A copies the F bit of the PACI payload NALU.
|
||||
func (p *H265PACIPacket) A() bool {
|
||||
const mask = 0b10000000 << 8
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// CType copies the Type field of the PACI payload NALU.
|
||||
func (p *H265PACIPacket) CType() uint8 {
|
||||
const mask = 0b01111110 << 8
|
||||
return uint8((p.paciHeaderFields & mask) >> (8 + 1))
|
||||
}
|
||||
|
||||
// PHSsize indicates the size of the PHES field.
|
||||
func (p *H265PACIPacket) PHSsize() uint8 {
|
||||
const mask = (0b00000001 << 8) | 0b11110000
|
||||
return uint8((p.paciHeaderFields & mask) >> 4)
|
||||
}
|
||||
|
||||
// F0 indicates the presence of a Temporal Scalability support extension in the PHES.
|
||||
func (p *H265PACIPacket) F0() bool {
|
||||
const mask = 0b00001000
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// F1 must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) F1() bool {
|
||||
const mask = 0b00000100
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// F2 must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) F2() bool {
|
||||
const mask = 0b00000010
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// Y must be zero, reserved for future extensions.
|
||||
func (p *H265PACIPacket) Y() bool {
|
||||
const mask = 0b00000001
|
||||
return (p.paciHeaderFields & mask) != 0
|
||||
}
|
||||
|
||||
// PHES contains header extensions. Its size is indicated by PHSsize.
|
||||
func (p *H265PACIPacket) PHES() []byte {
|
||||
return p.phes
|
||||
}
|
||||
|
||||
// Payload is a single NALU or NALU-like struct, not including the first two octets (header).
|
||||
func (p *H265PACIPacket) Payload() []byte {
|
||||
return p.payload
|
||||
}
|
||||
|
||||
// TSCI returns the Temporal Scalability Control Information extension, if present.
|
||||
func (p *H265PACIPacket) TSCI() *H265TSCI {
|
||||
if !p.F0() || p.PHSsize() < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tsci := H265TSCI((uint32(p.phes[0]) << 16) | (uint32(p.phes[1]) << 8) | uint32(p.phes[0]))
|
||||
return &tsci
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265PACIPacket this method is called upon.
|
||||
func (p *H265PACIPacket) Unmarshal(payload []byte) ([]byte, error) {
|
||||
// sizeof(headers)
|
||||
const totalHeaderSize = h265NaluHeaderSize + 2
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= totalHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), totalHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
if !payloadHeader.IsPACIPacket() {
|
||||
return nil, errInvalidH265PacketType
|
||||
}
|
||||
|
||||
paciHeaderFields := (uint16(payload[2]) << 8) | uint16(payload[3])
|
||||
payload = payload[4:]
|
||||
|
||||
p.paciHeaderFields = paciHeaderFields
|
||||
headerExtensionSize := p.PHSsize()
|
||||
|
||||
if len(payload) < int(headerExtensionSize)+1 {
|
||||
p.paciHeaderFields = 0
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.payloadHeader = payloadHeader
|
||||
|
||||
if headerExtensionSize > 0 {
|
||||
p.phes = payload[:headerExtensionSize]
|
||||
}
|
||||
|
||||
payload = payload[headerExtensionSize:]
|
||||
p.payload = payload
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *H265PACIPacket) isH265Packet() {}
|
||||
|
||||
//
|
||||
// Temporal Scalability Control Information
|
||||
//
|
||||
|
||||
// H265TSCI is a Temporal Scalability Control Information header extension.
|
||||
// Reference: https://datatracker.ietf.org/doc/html/rfc7798#section-4.5
|
||||
type H265TSCI uint32
|
||||
|
||||
// TL0PICIDX see RFC7798 for more details.
|
||||
func (h H265TSCI) TL0PICIDX() uint8 {
|
||||
const m1 = 0xFFFF0000
|
||||
const m2 = 0xFF00
|
||||
return uint8((((h & m1) >> 16) & m2) >> 8)
|
||||
}
|
||||
|
||||
// IrapPicID see RFC7798 for more details.
|
||||
func (h H265TSCI) IrapPicID() uint8 {
|
||||
const m1 = 0xFFFF0000
|
||||
const m2 = 0x00FF
|
||||
return uint8(((h & m1) >> 16) & m2)
|
||||
}
|
||||
|
||||
// S see RFC7798 for more details.
|
||||
func (h H265TSCI) S() bool {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b10000000
|
||||
return (uint8((h&m1)>>8) & m2) != 0
|
||||
}
|
||||
|
||||
// E see RFC7798 for more details.
|
||||
func (h H265TSCI) E() bool {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b01000000
|
||||
return (uint8((h&m1)>>8) & m2) != 0
|
||||
}
|
||||
|
||||
// RES see RFC7798 for more details.
|
||||
func (h H265TSCI) RES() uint8 {
|
||||
const m1 = 0xFF00
|
||||
const m2 = 0b00111111
|
||||
return uint8((h&m1)>>8) & m2
|
||||
}
|
||||
|
||||
//
|
||||
// H265 Packet interface
|
||||
//
|
||||
|
||||
type isH265Packet interface {
|
||||
isH265Packet()
|
||||
}
|
||||
|
||||
var (
|
||||
_ isH265Packet = (*H265FragmentationUnitPacket)(nil)
|
||||
_ isH265Packet = (*H265PACIPacket)(nil)
|
||||
_ isH265Packet = (*H265SingleNALUnitPacket)(nil)
|
||||
_ isH265Packet = (*H265AggregationPacket)(nil)
|
||||
)
|
||||
|
||||
//
|
||||
// Packet implementation
|
||||
//
|
||||
|
||||
// H265Packet represents a H265 packet, stored in the payload of an RTP packet.
|
||||
type H265Packet struct {
|
||||
packet isH265Packet
|
||||
mightNeedDONL bool
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// WithDONL can be called to specify whether or not DONL might be parsed.
|
||||
// DONL may need to be parsed if `sprop-max-don-diff` is greater than 0 on the RTP stream.
|
||||
func (p *H265Packet) WithDONL(value bool) {
|
||||
p.mightNeedDONL = value
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the H265Packet this method is called upon
|
||||
func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) {
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(payload) <= h265NaluHeaderSize {
|
||||
return nil, fmt.Errorf("%w: %d <= %v", errShortPacket, len(payload), h265NaluHeaderSize)
|
||||
}
|
||||
|
||||
payloadHeader := newH265NALUHeader(payload[0], payload[1])
|
||||
if payloadHeader.F() {
|
||||
return nil, errH265CorruptedPacket
|
||||
}
|
||||
|
||||
switch {
|
||||
case payloadHeader.IsPACIPacket():
|
||||
decoded := &H265PACIPacket{}
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
case payloadHeader.IsFragmentationUnit():
|
||||
decoded := &H265FragmentationUnitPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
case payloadHeader.IsAggregationPacket():
|
||||
decoded := &H265AggregationPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
|
||||
default:
|
||||
decoded := &H265SingleNALUnitPacket{}
|
||||
decoded.WithDONL(p.mightNeedDONL)
|
||||
|
||||
if _, err := decoded.Unmarshal(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.packet = decoded
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Packet returns the populated packet.
|
||||
// Must be casted to one of:
|
||||
// - *H265SingleNALUnitPacket
|
||||
// - *H265FragmentationUnitPacket
|
||||
// - *H265AggregationPacket
|
||||
// - *H265PACIPacket
|
||||
// nolint:golint
|
||||
func (p *H265Packet) Packet() isH265Packet {
|
||||
return p.packet
|
||||
}
|
||||
|
||||
// IsPartitionHead checks if this is the head of a packetized nalu stream.
|
||||
func (*H265Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
if H265NALUHeader(binary.BigEndian.Uint16(payload[0:2])).Type() == h265NaluFragmentationUnitType {
|
||||
return H265FragmentationUnitHeader(payload[2]).S()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
49
server/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
49
server/vendor/github.com/pion/rtp/codecs/opus_packet.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// OpusPayloader payloads Opus packets
|
||||
type OpusPayloader struct{}
|
||||
|
||||
// Payload fragments an Opus packet across one or more byte arrays
|
||||
func (p *OpusPayloader) Payload(_ uint16, payload []byte) [][]byte {
|
||||
if payload == nil {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
out := make([]byte, len(payload))
|
||||
copy(out, payload)
|
||||
return [][]byte{out}
|
||||
}
|
||||
|
||||
// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet
|
||||
type OpusPacket struct {
|
||||
Payload []byte
|
||||
|
||||
audioDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon
|
||||
func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
|
||||
if packet == nil {
|
||||
return nil, errNilPacket
|
||||
} else if len(packet) == 0 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.Payload = packet
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
// OpusPartitionHeadChecker checks Opus partition head.
|
||||
//
|
||||
// Deprecated: replaced by OpusPacket.IsPartitionHead()
|
||||
type OpusPartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the Opus partition.
|
||||
//
|
||||
// Deprecated: replaced by OpusPacket.IsPartitionHead()
|
||||
func (*OpusPartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&OpusPacket{}).IsPartitionHead(packet)
|
||||
}
|
||||
234
server/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
234
server/vendor/github.com/pion/rtp/codecs/vp8_packet.go
generated
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
// VP8Payloader payloads VP8 packets
|
||||
type VP8Payloader struct {
|
||||
EnablePictureID bool
|
||||
pictureID uint16
|
||||
}
|
||||
|
||||
const (
|
||||
vp8HeaderSize = 1
|
||||
)
|
||||
|
||||
// Payload fragments a VP8 packet across one or more byte arrays
|
||||
func (p *VP8Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* https://tools.ietf.org/html/rfc7741#section-4.2
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |X|R|N|S|R| PID | (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* X: |I|L|T|K| RSV | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PictureID | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TL0PICIDX | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* T/K: |TID|Y| KEYIDX | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* S: Start of VP8 partition. SHOULD be set to 1 when the first payload
|
||||
* octet of the RTP packet is the beginning of a new VP8 partition,
|
||||
* and MUST NOT be 1 otherwise. The S bit MUST be set to 1 for the
|
||||
* first packet of each encoded frame.
|
||||
*/
|
||||
|
||||
usingHeaderSize := vp8HeaderSize
|
||||
if p.EnablePictureID {
|
||||
switch {
|
||||
case p.pictureID == 0:
|
||||
case p.pictureID < 128:
|
||||
usingHeaderSize = vp8HeaderSize + 2
|
||||
default:
|
||||
usingHeaderSize = vp8HeaderSize + 3
|
||||
}
|
||||
}
|
||||
|
||||
maxFragmentSize := int(mtu) - usingHeaderSize
|
||||
|
||||
payloadData := payload
|
||||
payloadDataRemaining := len(payload)
|
||||
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
// Make sure the fragment/payload size is correct
|
||||
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
|
||||
return payloads
|
||||
}
|
||||
first := true
|
||||
for payloadDataRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
out := make([]byte, usingHeaderSize+currentFragmentSize)
|
||||
|
||||
if first {
|
||||
out[0] = 0x10
|
||||
first = false
|
||||
}
|
||||
if p.EnablePictureID {
|
||||
switch usingHeaderSize {
|
||||
case vp8HeaderSize:
|
||||
case vp8HeaderSize + 2:
|
||||
out[0] |= 0x80
|
||||
out[1] |= 0x80
|
||||
out[2] |= uint8(p.pictureID & 0x7F)
|
||||
case vp8HeaderSize + 3:
|
||||
out[0] |= 0x80
|
||||
out[1] |= 0x80
|
||||
out[2] |= 0x80 | uint8((p.pictureID>>8)&0x7F)
|
||||
out[3] |= uint8(p.pictureID & 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
copy(out[usingHeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
p.pictureID++
|
||||
p.pictureID &= 0x7FFF
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet
|
||||
type VP8Packet struct {
|
||||
// Required Header
|
||||
X uint8 /* extended control bits present */
|
||||
N uint8 /* when set to 1 this frame can be discarded */
|
||||
S uint8 /* start of VP8 partition */
|
||||
PID uint8 /* partition index */
|
||||
|
||||
// Extended control bits
|
||||
I uint8 /* 1 if PictureID is present */
|
||||
L uint8 /* 1 if TL0PICIDX is present */
|
||||
T uint8 /* 1 if TID is present */
|
||||
K uint8 /* 1 if KEYIDX is present */
|
||||
|
||||
// Optional extension
|
||||
PictureID uint16 /* 8 or 16 bits, picture ID */
|
||||
TL0PICIDX uint8 /* 8 bits temporal level zero index */
|
||||
TID uint8 /* 2 bits temporal layer index */
|
||||
Y uint8 /* 1 bit layer sync bit */
|
||||
KEYIDX uint8 /* 5 bits temporal key frame index */
|
||||
|
||||
Payload []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon
|
||||
func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) { //nolint: gocognit
|
||||
if payload == nil {
|
||||
return nil, errNilPacket
|
||||
}
|
||||
|
||||
payloadLen := len(payload)
|
||||
|
||||
payloadIndex := 0
|
||||
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.X = (payload[payloadIndex] & 0x80) >> 7
|
||||
p.N = (payload[payloadIndex] & 0x20) >> 5
|
||||
p.S = (payload[payloadIndex] & 0x10) >> 4
|
||||
p.PID = payload[payloadIndex] & 0x07
|
||||
|
||||
payloadIndex++
|
||||
|
||||
if p.X == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.I = (payload[payloadIndex] & 0x80) >> 7
|
||||
p.L = (payload[payloadIndex] & 0x40) >> 6
|
||||
p.T = (payload[payloadIndex] & 0x20) >> 5
|
||||
p.K = (payload[payloadIndex] & 0x10) >> 4
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.I = 0
|
||||
p.L = 0
|
||||
p.T = 0
|
||||
p.K = 0
|
||||
}
|
||||
|
||||
if p.I == 1 { // PID present?
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit
|
||||
if payloadIndex+1 >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.PictureID = (uint16(payload[payloadIndex]&0x7F) << 8) | uint16(payload[payloadIndex+1])
|
||||
payloadIndex += 2
|
||||
} else {
|
||||
p.PictureID = uint16(payload[payloadIndex])
|
||||
payloadIndex++
|
||||
}
|
||||
} else {
|
||||
p.PictureID = 0
|
||||
}
|
||||
|
||||
if p.L == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
p.TL0PICIDX = payload[payloadIndex]
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.TL0PICIDX = 0
|
||||
}
|
||||
|
||||
if p.T == 1 || p.K == 1 {
|
||||
if payloadIndex >= payloadLen {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
if p.T == 1 {
|
||||
p.TID = payload[payloadIndex] >> 6
|
||||
p.Y = (payload[payloadIndex] >> 5) & 0x1
|
||||
} else {
|
||||
p.TID = 0
|
||||
p.Y = 0
|
||||
}
|
||||
if p.K == 1 {
|
||||
p.KEYIDX = payload[payloadIndex] & 0x1F
|
||||
} else {
|
||||
p.KEYIDX = 0
|
||||
}
|
||||
payloadIndex++
|
||||
} else {
|
||||
p.TID = 0
|
||||
p.Y = 0
|
||||
p.KEYIDX = 0
|
||||
}
|
||||
|
||||
p.Payload = payload[payloadIndex:]
|
||||
return p.Payload, nil
|
||||
}
|
||||
|
||||
// VP8PartitionHeadChecker checks VP8 partition head
|
||||
//
|
||||
// Deprecated: replaced by VP8Packet.IsPartitionHead()
|
||||
type VP8PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP8 partition.
|
||||
//
|
||||
// Deprecated: replaced by VP8Packet.IsPartitionHead()
|
||||
func (*VP8PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&VP8Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP8 partition
|
||||
func (*VP8Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 1 {
|
||||
return false
|
||||
}
|
||||
return (payload[0] & 0x10) != 0
|
||||
}
|
||||
65
server/vendor/github.com/pion/rtp/codecs/vp9/bits.go
generated
vendored
Normal file
65
server/vendor/github.com/pion/rtp/codecs/vp9/bits.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package vp9
|
||||
|
||||
import "errors"
|
||||
|
||||
var errNotEnoughBits = errors.New("not enough bits")
|
||||
|
||||
func hasSpace(buf []byte, pos int, n int) error {
|
||||
if n > ((len(buf) * 8) - pos) {
|
||||
return errNotEnoughBits
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readFlag(buf []byte, pos *int) (bool, error) {
|
||||
err := hasSpace(buf, *pos, 1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return readFlagUnsafe(buf, pos), nil
|
||||
}
|
||||
|
||||
func readFlagUnsafe(buf []byte, pos *int) bool {
|
||||
b := (buf[*pos>>0x03] >> (7 - (*pos & 0x07))) & 0x01
|
||||
*pos++
|
||||
return b == 1
|
||||
}
|
||||
|
||||
func readBits(buf []byte, pos *int, n int) (uint64, error) {
|
||||
err := hasSpace(buf, *pos, n)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return readBitsUnsafe(buf, pos, n), nil
|
||||
}
|
||||
|
||||
func readBitsUnsafe(buf []byte, pos *int, n int) uint64 {
|
||||
res := 8 - (*pos & 0x07)
|
||||
if n < res {
|
||||
v := uint64((buf[*pos>>0x03] >> (res - n)) & (1<<n - 1))
|
||||
*pos += n
|
||||
return v
|
||||
}
|
||||
|
||||
v := uint64(buf[*pos>>0x03] & (1<<res - 1))
|
||||
*pos += res
|
||||
n -= res
|
||||
|
||||
for n >= 8 {
|
||||
v = (v << 8) | uint64(buf[*pos>>0x03])
|
||||
*pos += 8
|
||||
n -= 8
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
v = (v << n) | uint64(buf[*pos>>0x03]>>(8-n))
|
||||
*pos += n
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
221
server/vendor/github.com/pion/rtp/codecs/vp9/header.go
generated
vendored
Normal file
221
server/vendor/github.com/pion/rtp/codecs/vp9/header.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package vp9 contains a VP9 header parser.
|
||||
package vp9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidFrameMarker = errors.New("invalid frame marker")
|
||||
errWrongFrameSyncByte0 = errors.New("wrong frame_sync_byte_0")
|
||||
errWrongFrameSyncByte1 = errors.New("wrong frame_sync_byte_1")
|
||||
errWrongFrameSyncByte2 = errors.New("wrong frame_sync_byte_2")
|
||||
)
|
||||
|
||||
// HeaderColorConfig is the color_config member of an header.
|
||||
type HeaderColorConfig struct {
|
||||
TenOrTwelveBit bool
|
||||
BitDepth uint8
|
||||
ColorSpace uint8
|
||||
ColorRange bool
|
||||
SubsamplingX bool
|
||||
SubsamplingY bool
|
||||
}
|
||||
|
||||
func (c *HeaderColorConfig) unmarshal(profile uint8, buf []byte, pos *int) error {
|
||||
if profile >= 2 {
|
||||
var err error
|
||||
c.TenOrTwelveBit, err = readFlag(buf, pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.TenOrTwelveBit {
|
||||
c.BitDepth = 12
|
||||
} else {
|
||||
c.BitDepth = 10
|
||||
}
|
||||
} else {
|
||||
c.BitDepth = 8
|
||||
}
|
||||
|
||||
tmp, err := readBits(buf, pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ColorSpace = uint8(tmp)
|
||||
|
||||
if c.ColorSpace != 7 {
|
||||
var err error
|
||||
c.ColorRange, err = readFlag(buf, pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if profile == 1 || profile == 3 {
|
||||
err := hasSpace(buf, *pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SubsamplingX = readFlagUnsafe(buf, pos)
|
||||
c.SubsamplingY = readFlagUnsafe(buf, pos)
|
||||
*pos++
|
||||
} else {
|
||||
c.SubsamplingX = true
|
||||
c.SubsamplingY = true
|
||||
}
|
||||
} else {
|
||||
c.ColorRange = true
|
||||
|
||||
if profile == 1 || profile == 3 {
|
||||
c.SubsamplingX = false
|
||||
c.SubsamplingY = false
|
||||
|
||||
err := hasSpace(buf, *pos, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*pos++
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderFrameSize is the frame_size member of an header.
|
||||
type HeaderFrameSize struct {
|
||||
FrameWidthMinus1 uint16
|
||||
FrameHeightMinus1 uint16
|
||||
}
|
||||
|
||||
func (s *HeaderFrameSize) unmarshal(buf []byte, pos *int) error {
|
||||
err := hasSpace(buf, *pos, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.FrameWidthMinus1 = uint16(readBitsUnsafe(buf, pos, 16))
|
||||
s.FrameHeightMinus1 = uint16(readBitsUnsafe(buf, pos, 16))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Header is a VP9 Frame header.
|
||||
// Specification:
|
||||
// https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf
|
||||
type Header struct {
|
||||
Profile uint8
|
||||
ShowExistingFrame bool
|
||||
FrameToShowMapIdx uint8
|
||||
NonKeyFrame bool
|
||||
ShowFrame bool
|
||||
ErrorResilientMode bool
|
||||
ColorConfig *HeaderColorConfig
|
||||
FrameSize *HeaderFrameSize
|
||||
}
|
||||
|
||||
// Unmarshal decodes a Header.
|
||||
func (h *Header) Unmarshal(buf []byte) error {
|
||||
pos := 0
|
||||
|
||||
err := hasSpace(buf, pos, 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameMarker := readBitsUnsafe(buf, &pos, 2)
|
||||
if frameMarker != 2 {
|
||||
return errInvalidFrameMarker
|
||||
}
|
||||
|
||||
profileLowBit := uint8(readBitsUnsafe(buf, &pos, 1))
|
||||
profileHighBit := uint8(readBitsUnsafe(buf, &pos, 1))
|
||||
h.Profile = profileHighBit<<1 + profileLowBit
|
||||
|
||||
if h.Profile == 3 {
|
||||
err = hasSpace(buf, pos, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pos++
|
||||
}
|
||||
|
||||
h.ShowExistingFrame, err = readFlag(buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.ShowExistingFrame {
|
||||
var tmp uint64
|
||||
tmp, err = readBits(buf, &pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.FrameToShowMapIdx = uint8(tmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = hasSpace(buf, pos, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.NonKeyFrame = readFlagUnsafe(buf, &pos)
|
||||
h.ShowFrame = readFlagUnsafe(buf, &pos)
|
||||
h.ErrorResilientMode = readFlagUnsafe(buf, &pos)
|
||||
|
||||
if !h.NonKeyFrame {
|
||||
err := hasSpace(buf, pos, 24)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
frameSyncByte0 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte0 != 0x49 {
|
||||
return errWrongFrameSyncByte0
|
||||
}
|
||||
|
||||
frameSyncByte1 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte1 != 0x83 {
|
||||
return errWrongFrameSyncByte1
|
||||
}
|
||||
|
||||
frameSyncByte2 := uint8(readBitsUnsafe(buf, &pos, 8))
|
||||
if frameSyncByte2 != 0x42 {
|
||||
return errWrongFrameSyncByte2
|
||||
}
|
||||
|
||||
h.ColorConfig = &HeaderColorConfig{}
|
||||
err = h.ColorConfig.unmarshal(h.Profile, buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FrameSize = &HeaderFrameSize{}
|
||||
err = h.FrameSize.unmarshal(buf, &pos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Width returns the video width.
|
||||
func (h Header) Width() uint16 {
|
||||
if h.FrameSize == nil {
|
||||
return 0
|
||||
}
|
||||
return h.FrameSize.FrameWidthMinus1 + 1
|
||||
}
|
||||
|
||||
// Height returns the video height.
|
||||
func (h Header) Height() uint16 {
|
||||
if h.FrameSize == nil {
|
||||
return 0
|
||||
}
|
||||
return h.FrameSize.FrameHeightMinus1 + 1
|
||||
}
|
||||
511
server/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
511
server/vendor/github.com/pion/rtp/codecs/vp9_packet.go
generated
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"github.com/pion/randutil"
|
||||
"github.com/pion/rtp/codecs/vp9"
|
||||
)
|
||||
|
||||
// Use global random generator to properly seed by crypto grade random.
|
||||
var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
|
||||
|
||||
// VP9Payloader payloads VP9 packets
|
||||
type VP9Payloader struct {
|
||||
// whether to use flexible mode or non-flexible mode.
|
||||
FlexibleMode bool
|
||||
|
||||
// InitialPictureIDFn is a function that returns random initial picture ID.
|
||||
InitialPictureIDFn func() uint16
|
||||
|
||||
pictureID uint16
|
||||
initialized bool
|
||||
}
|
||||
|
||||
const (
|
||||
maxSpatialLayers = 5
|
||||
maxVP9RefPics = 3
|
||||
)
|
||||
|
||||
// Payload fragments an VP9 packet across one or more byte arrays
|
||||
func (p *VP9Payloader) Payload(mtu uint16, payload []byte) [][]byte {
|
||||
if !p.initialized {
|
||||
if p.InitialPictureIDFn == nil {
|
||||
p.InitialPictureIDFn = func() uint16 {
|
||||
return uint16(globalMathRandomGenerator.Intn(0x7FFF))
|
||||
}
|
||||
}
|
||||
p.pictureID = p.InitialPictureIDFn() & 0x7FFF
|
||||
p.initialized = true
|
||||
}
|
||||
|
||||
var payloads [][]byte
|
||||
if p.FlexibleMode {
|
||||
payloads = p.payloadFlexible(mtu, payload)
|
||||
} else {
|
||||
payloads = p.payloadNonFlexible(mtu, payload)
|
||||
}
|
||||
|
||||
p.pictureID++
|
||||
if p.pictureID >= 0x8000 {
|
||||
p.pictureID = 0
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
func (p *VP9Payloader) payloadFlexible(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* Flexible mode (F=1)
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |I|P|L|F|B|E|V|Z| (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* M: | EXTENDED PID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+ -\
|
||||
* P,F: | P_DIFF |N| (CONDITIONALLY REQUIRED) - up to 3 times
|
||||
* +-+-+-+-+-+-+-+-+ -/
|
||||
* V: | SS |
|
||||
* | .. |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
headerSize := 3
|
||||
maxFragmentSize := int(mtu) - headerSize
|
||||
payloadDataRemaining := len(payload)
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
if min(maxFragmentSize, payloadDataRemaining) <= 0 {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
out := make([]byte, headerSize+currentFragmentSize)
|
||||
|
||||
out[0] = 0x90 // F=1, I=1
|
||||
if payloadDataIndex == 0 {
|
||||
out[0] |= 0x08 // B=1
|
||||
}
|
||||
if payloadDataRemaining == currentFragmentSize {
|
||||
out[0] |= 0x04 // E=1
|
||||
}
|
||||
|
||||
out[1] = byte(p.pictureID>>8) | 0x80
|
||||
out[2] = byte(p.pictureID)
|
||||
|
||||
copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
func (p *VP9Payloader) payloadNonFlexible(mtu uint16, payload []byte) [][]byte {
|
||||
/*
|
||||
* Non-flexible mode (F=0)
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* |I|P|L|F|B|E|V|Z| (REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* M: | EXTENDED PID | (RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* | TL0PICIDX | (CONDITIONALLY REQUIRED)
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* V: | SS |
|
||||
* | .. |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
var h vp9.Header
|
||||
err := h.Unmarshal(payload)
|
||||
if err != nil {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
payloadDataRemaining := len(payload)
|
||||
payloadDataIndex := 0
|
||||
var payloads [][]byte
|
||||
|
||||
for payloadDataRemaining > 0 {
|
||||
var headerSize int
|
||||
if !h.NonKeyFrame && payloadDataIndex == 0 {
|
||||
headerSize = 3 + 8
|
||||
} else {
|
||||
headerSize = 3
|
||||
}
|
||||
|
||||
maxFragmentSize := int(mtu) - headerSize
|
||||
currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
|
||||
if currentFragmentSize <= 0 {
|
||||
return [][]byte{}
|
||||
}
|
||||
|
||||
out := make([]byte, headerSize+currentFragmentSize)
|
||||
|
||||
out[0] = 0x80 | 0x01 // I=1, Z=1
|
||||
|
||||
if h.NonKeyFrame {
|
||||
out[0] |= 0x40 // P=1
|
||||
}
|
||||
if payloadDataIndex == 0 {
|
||||
out[0] |= 0x08 // B=1
|
||||
}
|
||||
if payloadDataRemaining == currentFragmentSize {
|
||||
out[0] |= 0x04 // E=1
|
||||
}
|
||||
|
||||
out[1] = byte(p.pictureID>>8) | 0x80
|
||||
out[2] = byte(p.pictureID)
|
||||
off := 3
|
||||
|
||||
if !h.NonKeyFrame && payloadDataIndex == 0 {
|
||||
out[0] |= 0x02 // V=1
|
||||
out[off] = 0x10 | 0x08 // N_S=0, Y=1, G=1
|
||||
off++
|
||||
|
||||
width := h.Width()
|
||||
out[off] = byte(width >> 8)
|
||||
off++
|
||||
out[off] = byte(width & 0xFF)
|
||||
off++
|
||||
|
||||
height := h.Height()
|
||||
out[off] = byte(height >> 8)
|
||||
off++
|
||||
out[off] = byte(height & 0xFF)
|
||||
off++
|
||||
|
||||
out[off] = 0x01 // N_G=1
|
||||
off++
|
||||
|
||||
out[off] = 1<<4 | 1<<2 // TID=0, U=1, R=1
|
||||
off++
|
||||
|
||||
out[off] = 0x01 // P_DIFF=1
|
||||
}
|
||||
|
||||
copy(out[headerSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
|
||||
payloads = append(payloads, out)
|
||||
|
||||
payloadDataRemaining -= currentFragmentSize
|
||||
payloadDataIndex += currentFragmentSize
|
||||
}
|
||||
|
||||
return payloads
|
||||
}
|
||||
|
||||
// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet
|
||||
type VP9Packet struct {
|
||||
// Required header
|
||||
I bool // PictureID is present
|
||||
P bool // Inter-picture predicted frame
|
||||
L bool // Layer indices is present
|
||||
F bool // Flexible mode
|
||||
B bool // Start of a frame
|
||||
E bool // End of a frame
|
||||
V bool // Scalability structure (SS) data present
|
||||
Z bool // Not a reference frame for upper spatial layers
|
||||
|
||||
// Recommended headers
|
||||
PictureID uint16 // 7 or 16 bits, picture ID
|
||||
|
||||
// Conditionally recommended headers
|
||||
TID uint8 // Temporal layer ID
|
||||
U bool // Switching up point
|
||||
SID uint8 // Spatial layer ID
|
||||
D bool // Inter-layer dependency used
|
||||
|
||||
// Conditionally required headers
|
||||
PDiff []uint8 // Reference index (F=1)
|
||||
TL0PICIDX uint8 // Temporal layer zero index (F=0)
|
||||
|
||||
// Scalability structure headers
|
||||
NS uint8 // N_S + 1 indicates the number of spatial layers present in the VP9 stream
|
||||
Y bool // Each spatial layer's frame resolution present
|
||||
G bool // PG description present flag.
|
||||
NG uint8 // N_G indicates the number of pictures in a Picture Group (PG)
|
||||
Width []uint16
|
||||
Height []uint16
|
||||
PGTID []uint8 // Temporal layer ID of pictures in a Picture Group
|
||||
PGU []bool // Switching up point of pictures in a Picture Group
|
||||
PGPDiff [][]uint8 // Reference indecies of pictures in a Picture Group
|
||||
|
||||
Payload []byte
|
||||
|
||||
videoDepacketizer
|
||||
}
|
||||
|
||||
// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon
|
||||
func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
|
||||
if packet == nil {
|
||||
return nil, errNilPacket
|
||||
}
|
||||
if len(packet) < 1 {
|
||||
return nil, errShortPacket
|
||||
}
|
||||
|
||||
p.I = packet[0]&0x80 != 0
|
||||
p.P = packet[0]&0x40 != 0
|
||||
p.L = packet[0]&0x20 != 0
|
||||
p.F = packet[0]&0x10 != 0
|
||||
p.B = packet[0]&0x08 != 0
|
||||
p.E = packet[0]&0x04 != 0
|
||||
p.V = packet[0]&0x02 != 0
|
||||
p.Z = packet[0]&0x01 != 0
|
||||
|
||||
pos := 1
|
||||
var err error
|
||||
|
||||
if p.I {
|
||||
pos, err = p.parsePictureID(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.L {
|
||||
pos, err = p.parseLayerInfo(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.F && p.P {
|
||||
pos, err = p.parseRefIndices(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if p.V {
|
||||
pos, err = p.parseSSData(packet, pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p.Payload = packet[pos:]
|
||||
return p.Payload, nil
|
||||
}
|
||||
|
||||
// Picture ID:
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* I: |M| PICTURE ID | M:0 => picture id is 7 bits.
|
||||
* +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
|
||||
* M: | EXTENDED PID |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.PictureID = uint16(packet[pos] & 0x7F)
|
||||
if packet[pos]&0x80 != 0 {
|
||||
pos++
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
p.PictureID = p.PictureID<<8 | uint16(packet[pos])
|
||||
}
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
func (p *VP9Packet) parseLayerInfo(packet []byte, pos int) (int, error) {
|
||||
pos, err := p.parseLayerInfoCommon(packet, pos)
|
||||
if err != nil {
|
||||
return pos, err
|
||||
}
|
||||
|
||||
if p.F {
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
return p.parseLayerInfoNonFlexibleMode(packet, pos)
|
||||
}
|
||||
|
||||
// Layer indices (flexible mode):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | T |U| S |D|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.TID = packet[pos] >> 5
|
||||
p.U = packet[pos]&0x10 != 0
|
||||
p.SID = (packet[pos] >> 1) & 0x7
|
||||
p.D = packet[pos]&0x01 != 0
|
||||
|
||||
if p.SID >= maxSpatialLayers {
|
||||
return pos, errTooManySpatialLayers
|
||||
}
|
||||
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Layer indices (non-flexible mode):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* L: | T |U| S |D|
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* | TL0PICIDX |
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
**/
|
||||
func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.TL0PICIDX = packet[pos]
|
||||
pos++
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Reference indices:
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
|
||||
* P,F: | P_DIFF |N| up to 3 times has to be specified.
|
||||
* +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
|
||||
* current P_DIFF.
|
||||
**/
|
||||
func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) {
|
||||
for {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
p.PDiff = append(p.PDiff, packet[pos]>>1)
|
||||
if packet[pos]&0x01 == 0 {
|
||||
break
|
||||
}
|
||||
if len(p.PDiff) >= maxVP9RefPics {
|
||||
return pos, errTooManyPDiff
|
||||
}
|
||||
pos++
|
||||
}
|
||||
pos++
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// Scalability structure (SS):
|
||||
/*
|
||||
* +-+-+-+-+-+-+-+-+
|
||||
* V: | N_S |Y|G|-|-|-|
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* Y: | WIDTH | (OPTIONAL) .
|
||||
* + .
|
||||
* | | (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ . N_S + 1 times
|
||||
* | HEIGHT | (OPTIONAL) .
|
||||
* + .
|
||||
* | | (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* G: | N_G | (OPTIONAL)
|
||||
* +-+-+-+-+-+-+-+-+ -|
|
||||
* N_G: | T |U| R |-|-| (OPTIONAL) .
|
||||
* +-+-+-+-+-+-+-+-+ -| . N_G times
|
||||
* | P_DIFF | (OPTIONAL) . R times .
|
||||
* +-+-+-+-+-+-+-+-+ -| -|
|
||||
**/
|
||||
func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.NS = packet[pos] >> 5
|
||||
p.Y = packet[pos]&0x10 != 0
|
||||
p.G = (packet[pos]>>1)&0x7 != 0
|
||||
pos++
|
||||
|
||||
NS := p.NS + 1
|
||||
p.NG = 0
|
||||
|
||||
if p.Y {
|
||||
p.Width = make([]uint16, NS)
|
||||
p.Height = make([]uint16, NS)
|
||||
for i := 0; i < int(NS); i++ {
|
||||
if len(packet) <= (pos + 3) {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.Width[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
|
||||
pos += 2
|
||||
p.Height[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
|
||||
pos += 2
|
||||
}
|
||||
}
|
||||
|
||||
if p.G {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.NG = packet[pos]
|
||||
pos++
|
||||
}
|
||||
|
||||
for i := 0; i < int(p.NG); i++ {
|
||||
if len(packet) <= pos {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
p.PGTID = append(p.PGTID, packet[pos]>>5)
|
||||
p.PGU = append(p.PGU, packet[pos]&0x10 != 0)
|
||||
R := (packet[pos] >> 2) & 0x3
|
||||
pos++
|
||||
|
||||
p.PGPDiff = append(p.PGPDiff, []uint8{})
|
||||
|
||||
if len(packet) <= (pos + int(R) - 1) {
|
||||
return pos, errShortPacket
|
||||
}
|
||||
|
||||
for j := 0; j < int(R); j++ {
|
||||
p.PGPDiff[i] = append(p.PGPDiff[i], packet[pos])
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return pos, nil
|
||||
}
|
||||
|
||||
// VP9PartitionHeadChecker checks VP9 partition head.
|
||||
//
|
||||
// Deprecated: replaced by VP9Packet.IsPartitionHead()
|
||||
type VP9PartitionHeadChecker struct{}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP9 partition.
|
||||
//
|
||||
// Deprecated: replaced by VP9Packet.IsPartitionHead()
|
||||
func (*VP9PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
|
||||
return (&VP9Packet{}).IsPartitionHead(packet)
|
||||
}
|
||||
|
||||
// IsPartitionHead checks whether if this is a head of the VP9 partition
|
||||
func (*VP9Packet) IsPartitionHead(payload []byte) bool {
|
||||
if len(payload) < 1 {
|
||||
return false
|
||||
}
|
||||
return (payload[0] & 0x08) != 0
|
||||
}
|
||||
Reference in New Issue
Block a user