vendor.github.com.pion.rtp.codecs.av1_packet.go Maven / Gradle / Ivy
The newest version!
// SPDX-FileCopyrightText: 2023 The Pion community
// 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
}
// 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
}
currentIndex := uint(1)
p.OBUElements = [][]byte{}
var (
obuElementLength, bytesRead uint
err error
)
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 {
obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:])
if err != nil {
return nil, err
}
}
currentIndex += bytesRead
if uint(len(payload)) < currentIndex+obuElementLength {
return nil, errShortPacket
}
p.OBUElements = append(p.OBUElements, payload[currentIndex:currentIndex+obuElementLength])
currentIndex += obuElementLength
}
return payload[1:], nil
}