Squashed 'livekit-server/' content from commit 154b4d26

git-subtree-dir: livekit-server
git-subtree-split: 154b4d26b769c68a03c096124094b97bf61a996f
This commit is contained in:
2026-06-25 14:35:28 +09:00
commit 0da97ebd21
339 changed files with 114111 additions and 0 deletions
@@ -0,0 +1,114 @@
// Copyright 2024 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package abscapturetime
import (
"encoding/binary"
"errors"
"time"
"github.com/livekit/mediatransportutil"
)
const (
AbsCaptureTimeURI = "http://www.webrtc.org/experiments/rtp-hdrext/abs-capture-time"
)
var (
errInvalidData = errors.New("invalid data")
errTooSmall = errors.New("buffer too small")
)
// Reference: https://webrtc.googlesource.com/src/+/refs/heads/main/docs/native-code/rtp-hdrext/abs-capture-time/
//
// Data layout of the shortened version of abs-capture-time with a 1-byte header + 8 bytes of data:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=7 | absolute capture timestamp (bit 0-23) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | absolute capture timestamp (bit 24-55) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... (56-63) |
// +-+-+-+-+-+-+-+-+
//
//Data layout of the extended version of abs-capture-time with a 1-byte header + 16 bytes of data:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=15| absolute capture timestamp (bit 0-23) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | absolute capture timestamp (bit 24-55) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... (56-63) | estimated capture clock offset (bit 0-23) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | estimated capture clock offset (bit 24-55) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... (56-63) |
// +-+-+-+-+-+-+-+-+
type AbsCaptureTime struct {
absoluteCaptureTimestamp mediatransportutil.NtpTime
estimatedCaptureClockOffset int64
}
func AbsCaptureTimeFromValue(absoluteCaptureTimestamp uint64, estimatedCaptureClockOffset int64) *AbsCaptureTime {
return &AbsCaptureTime{
absoluteCaptureTimestamp: mediatransportutil.NtpTime(absoluteCaptureTimestamp),
estimatedCaptureClockOffset: estimatedCaptureClockOffset,
}
}
func (a *AbsCaptureTime) Rewrite(offset time.Duration) error {
if a.absoluteCaptureTimestamp == 0 {
return errInvalidData
}
capturedAt := a.absoluteCaptureTimestamp.Time().Add(offset)
a.absoluteCaptureTimestamp = mediatransportutil.ToNtpTime(capturedAt)
a.estimatedCaptureClockOffset = 0
return nil
}
func (a *AbsCaptureTime) Marshal() ([]byte, error) {
if a.absoluteCaptureTimestamp == 0 {
return nil, errInvalidData
}
size := 8
if a.estimatedCaptureClockOffset != 0 {
size += 8
}
marshalled := make([]byte, size)
binary.BigEndian.PutUint64(marshalled, uint64(a.absoluteCaptureTimestamp))
if a.estimatedCaptureClockOffset != 0 {
binary.BigEndian.PutUint64(marshalled[8:], uint64(a.estimatedCaptureClockOffset))
}
return marshalled, nil
}
func (a *AbsCaptureTime) Unmarshal(marshalled []byte) error {
if len(marshalled) < 8 {
return errTooSmall
}
a.absoluteCaptureTimestamp = mediatransportutil.NtpTime(binary.BigEndian.Uint64(marshalled))
if len(marshalled) >= 16 {
a.estimatedCaptureClockOffset = int64(binary.BigEndian.Uint64(marshalled[8:]))
}
return nil
}
@@ -0,0 +1,138 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"errors"
"io"
)
type BitStreamReader struct {
buf []byte
pos int
remainingBits int
}
func NewBitStreamReader(buf []byte) *BitStreamReader {
return &BitStreamReader{buf: buf, remainingBits: len(buf) * 8}
}
func (b *BitStreamReader) RemainingBits() int {
return b.remainingBits
}
// Reads `bits` from the bitstream. `bits` must be in range [0, 64].
// Returns an unsigned integer in range [0, 2^bits - 1].
// On failure sets `BitstreamReader` into the failure state and returns 0.
func (b *BitStreamReader) ReadBits(bits int) (uint64, error) {
if bits < 0 || bits > 64 {
return 0, errors.New("invalid number of bits, expected 0-64")
}
if b.remainingBits < bits {
b.remainingBits -= bits
return 0, io.EOF
}
remainingBitsInFirstByte := b.remainingBits % 8
b.remainingBits -= bits
if bits < remainingBitsInFirstByte {
// Reading fewer bits than what's left in the current byte, just
// return the portion of this byte that is needed.
offset := remainingBitsInFirstByte - bits
return uint64((b.buf[b.pos] >> offset) & ((1 << bits) - 1)), nil
}
var result uint64
if remainingBitsInFirstByte > 0 {
// Read all bits that were left in the current byte and consume that byte.
bits -= remainingBitsInFirstByte
mask := byte((1 << remainingBitsInFirstByte) - 1)
result = uint64(b.buf[b.pos]&mask) << bits
b.pos++
}
// Read as many full bytes as we can.
for bits >= 8 {
bits -= 8
result |= uint64(b.buf[b.pos]) << bits
b.pos++
}
// Whatever is left to read is smaller than a byte, so grab just the needed
// bits and shift them into the lowest bits.
if bits > 0 {
result |= uint64(b.buf[b.pos] >> (8 - bits))
}
return result, nil
}
func (b *BitStreamReader) ReadBool() (bool, error) {
val, err := b.ReadBits(1)
return val != 0, err
}
func (b *BitStreamReader) Ok() bool {
return b.remainingBits >= 0
}
func (b *BitStreamReader) Invalidate() {
b.remainingBits = -1
}
// Reads value in range [0, `num_values` - 1].
// This encoding is similar to ReadBits(val, Ceil(Log2(num_values)),
// but reduces wastage incurred when encoding non-power of two value ranges
// Non symmetric values are encoded as:
// 1) n = bit_width(num_values)
// 2) k = (1 << n) - num_values
// Value v in range [0, k - 1] is encoded in (n-1) bits.
// Value v in range [k, num_values - 1] is encoded as (v+k) in n bits.
// https://aomediacodec.github.io/av1-spec/#nsn
func (b *BitStreamReader) ReadNonSymmetric(numValues uint32) (uint32, error) {
if numValues >= (uint32(1) << 31) {
return 0, errors.New("invalid number of values, expected 0-2^31")
}
width := bitwidth(numValues)
numMinBitsValues := (uint32(1) << width) - numValues
val, err := b.ReadBits(width - 1)
if err != nil {
return 0, err
}
if val < uint64(numMinBitsValues) {
return uint32(val), nil
}
bit, err := b.ReadBits(1)
if err != nil {
return 0, err
}
return uint32((val << 1) + bit - uint64(numMinBitsValues)), nil
}
func (b *BitStreamReader) BytesRead() int {
if b.remainingBits%8 > 0 {
return b.pos + 1
}
return b.pos
}
func bitwidth(n uint32) int {
var w int
for n != 0 {
n >>= 1
w++
}
return w
}
@@ -0,0 +1,132 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"errors"
"fmt"
)
type BitStreamWriter struct {
buf []byte
pos int
bitOffset int // bit offset in the current byte
}
func NewBitStreamWriter(buf []byte) *BitStreamWriter {
return &BitStreamWriter{buf: buf}
}
func (w *BitStreamWriter) RemainingBits() int {
return (len(w.buf)-w.pos)*8 - w.bitOffset
}
func (w *BitStreamWriter) WriteBits(val uint64, bitCount int) error {
if bitCount > w.RemainingBits() {
return errors.New("insufficient space")
}
totalBits := bitCount
// push bits to the highest bits of uint64
val <<= 64 - bitCount
buf := w.buf[w.pos:]
// The first byte is relatively special; the bit offset to write to may put us
// in the middle of the byte, and the total bit count to write may require we
// save the bits at the end of the byte.
remainingBitsInCurrentByte := 8 - w.bitOffset
bitsInFirstByte := bitCount
if bitsInFirstByte > remainingBitsInCurrentByte {
bitsInFirstByte = remainingBitsInCurrentByte
}
buf[0] = w.writePartialByte(uint8(val>>56), bitsInFirstByte, buf[0], w.bitOffset)
if bitCount <= remainingBitsInCurrentByte {
// no bit left to write
return w.consumeBits(totalBits)
}
// write the rest of the bits
val <<= bitsInFirstByte
buf = buf[1:]
bitCount -= bitsInFirstByte
for bitCount >= 8 {
buf[0] = uint8(val >> 56)
buf = buf[1:]
val <<= 8
bitCount -= 8
}
// write the last bits
if bitCount > 0 {
buf[0] = w.writePartialByte(uint8(val>>56), bitCount, buf[0], 0)
}
return w.consumeBits(totalBits)
}
func (w *BitStreamWriter) consumeBits(bitCount int) error {
if bitCount > w.RemainingBits() {
return errors.New("insufficient space")
}
w.pos += (w.bitOffset + bitCount) / 8
w.bitOffset = (w.bitOffset + bitCount) % 8
return nil
}
func (w *BitStreamWriter) writePartialByte(source uint8, sourceBitCount int, target uint8, targetBitOffset int) uint8 {
// if !(targetBitOffset < 8 && sourceBitCount <= (8-targetBitOffset)) {
// return fmt.Errorf("invalid argument, source %d, sourceBitCount %d, target %d, targetBitOffset %d", source, sourceBitCount, target, targetBitOffset)
// }
// generate mask for bits to overwrite, shift source bits to highest bits, then position to target bit offset
mask := uint8(0xff<<(8-sourceBitCount)) >> uint8(targetBitOffset)
// clear target bits and write source bits
return (target & ^mask) | (source >> targetBitOffset)
}
func (w *BitStreamWriter) WriteNonSymmetric(val, numValues uint32) error {
if !(val < numValues && numValues <= 1<<31) {
return fmt.Errorf("invalid argument, val %d, numValues %d", val, numValues)
}
if numValues == 1 {
// When there is only one possible value, it requires zero bits to store it.
// But WriteBits doesn't support writing zero bits.
return nil
}
countBits := bitwidth(numValues)
numMinBitsValues := (uint32(1) << countBits) - numValues
if val < numMinBitsValues {
return w.WriteBits(uint64(val), countBits-1)
} else {
return w.WriteBits(uint64(val+numMinBitsValues), countBits)
}
}
func SizeNonSymmetricBits(val, numValues uint32) int {
countBits := bitwidth(numValues)
numMinBitsValues := (uint32(1) << countBits) - numValues
if val < numMinBitsValues {
return countBits - 1
} else {
return countBits
}
}
@@ -0,0 +1,199 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"fmt"
"math"
"strconv"
)
// DependencyDescriptorExtension is a extension payload format in
// https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension
func formatBitmask(b *uint32) string {
if b == nil {
return "-"
}
return strconv.FormatInt(int64(*b), 2)
}
// ------------------------------------------------------------------------------
type DependencyDescriptorExtension struct {
Descriptor *DependencyDescriptor
Structure *FrameDependencyStructure
}
func (d *DependencyDescriptorExtension) Marshal() ([]byte, error) {
return d.MarshalWithActiveChains(^uint32(0))
}
func (d *DependencyDescriptorExtension) MarshalWithActiveChains(activeChains uint32) ([]byte, error) {
writer, err := NewDependencyDescriptorWriter(nil, d.Structure, activeChains, d.Descriptor)
if err != nil {
return nil, err
}
buf := make([]byte, int(math.Ceil(float64(writer.ValueSizeBits())/8)))
writer.ResetBuf(buf)
if err = writer.Write(); err != nil {
return nil, err
}
return buf, nil
}
func (d *DependencyDescriptorExtension) Unmarshal(buf []byte) (int, error) {
reader := NewDependencyDescriptorReader(buf, d.Structure, d.Descriptor)
return reader.Parse()
}
// ------------------------------------------------------------------------------
const (
MaxSpatialIds = 4
MaxTemporalIds = 8
MaxDecodeTargets = 32
MaxTemplates = 64
AllChainsAreActive = uint32(0)
ExtensionURI = "https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension"
)
// ------------------------------------------------------------------------------
type DependencyDescriptor struct {
FirstPacketInFrame bool
LastPacketInFrame bool
FrameNumber uint16
FrameDependencies *FrameDependencyTemplate
Resolution *RenderResolution
ActiveDecodeTargetsBitmask *uint32
AttachedStructure *FrameDependencyStructure
}
func (d *DependencyDescriptor) MarshalSize() (int, error) {
return d.MarshalSizeWithActiveChains(^uint32(0))
}
func (d *DependencyDescriptor) MarshalSizeWithActiveChains(activeChains uint32) (int, error) {
writer, err := NewDependencyDescriptorWriter(nil, d.AttachedStructure, activeChains, d)
if err != nil {
return 0, err
}
return int(math.Ceil(float64(writer.ValueSizeBits()) / 8)), nil
}
func (d *DependencyDescriptor) String() string {
resolution, dependencies := "-", "-"
if d.Resolution != nil {
resolution = fmt.Sprintf("%+v", *d.Resolution)
}
if d.FrameDependencies != nil {
dependencies = fmt.Sprintf("%+v", *d.FrameDependencies)
}
return fmt.Sprintf("DependencyDescriptor{FirstPacketInFrame: %v, LastPacketInFrame: %v, FrameNumber: %v, FrameDependencies: %s, Resolution: %s, ActiveDecodeTargetsBitmask: %v, AttachedStructure: %v}",
d.FirstPacketInFrame, d.LastPacketInFrame, d.FrameNumber, dependencies, resolution, formatBitmask(d.ActiveDecodeTargetsBitmask), d.AttachedStructure)
}
// ------------------------------------------------------------------------------
// Relationship of a frame to a Decode target.
type DecodeTargetIndication int
const (
DecodeTargetNotPresent DecodeTargetIndication = iota // DecodeTargetInfo symbol '-'
DecodeTargetDiscardable // DecodeTargetInfo symbol 'D'
DecodeTargetSwitch // DecodeTargetInfo symbol 'S'
DecodeTargetRequired // DecodeTargetInfo symbol 'R'
)
func (i DecodeTargetIndication) String() string {
switch i {
case DecodeTargetNotPresent:
return "-"
case DecodeTargetDiscardable:
return "D"
case DecodeTargetSwitch:
return "S"
case DecodeTargetRequired:
return "R"
default:
return "Unknown"
}
}
// ------------------------------------------------------------------------------
type FrameDependencyTemplate struct {
SpatialId int
TemporalId int
DecodeTargetIndications []DecodeTargetIndication
FrameDiffs []int
ChainDiffs []int
}
func (t *FrameDependencyTemplate) Clone() *FrameDependencyTemplate {
t2 := &FrameDependencyTemplate{
SpatialId: t.SpatialId,
TemporalId: t.TemporalId,
}
t2.DecodeTargetIndications = make([]DecodeTargetIndication, len(t.DecodeTargetIndications))
copy(t2.DecodeTargetIndications, t.DecodeTargetIndications)
t2.FrameDiffs = make([]int, len(t.FrameDiffs))
copy(t2.FrameDiffs, t.FrameDiffs)
t2.ChainDiffs = make([]int, len(t.ChainDiffs))
copy(t2.ChainDiffs, t.ChainDiffs)
return t2
}
// ------------------------------------------------------------------------------
type FrameDependencyStructure struct {
StructureId int
NumDecodeTargets int
NumChains int
// If chains are used (num_chains > 0), maps decode target index into index of
// the chain protecting that target.
DecodeTargetProtectedByChain []int
Resolutions []RenderResolution
Templates []*FrameDependencyTemplate
}
func (f *FrameDependencyStructure) String() string {
str := fmt.Sprintf("FrameDependencyStructure{StructureId: %v, NumDecodeTargets: %v, NumChains: %v, DecodeTargetProtectedByChain: %v, Resolutions: %+v, Templates: [",
f.StructureId, f.NumDecodeTargets, f.NumChains, f.DecodeTargetProtectedByChain, f.Resolutions)
// templates
for _, t := range f.Templates {
str += fmt.Sprintf("%+v, ", t)
}
str += "]}"
return str
}
// ------------------------------------------------------------------------------
type RenderResolution struct {
Width int
Height int
}
// ------------------------------------------------------------------------------
@@ -0,0 +1,65 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"encoding/hex"
"testing"
)
func TestDependencyDescriptorUnmarshal(t *testing.T) {
// hex bytes from traffic capture
hexes := []string{
"c1017280081485214eafffaaaa863cf0430c10c302afc0aaa0063c00430010c002a000a80006000040001d954926e082b04a0941b820ac1282503157f974000ca864330e222222eca8655304224230eca877530077004200ef008601df010d",
"86017340fc",
"46017340fc",
"c3017540fc",
"88017640fc",
"48017640fc",
"c2017840fc",
//
"c1017280081485214eafffaaaa863cf0430c10c302afc0aaa0063c00430010c002a000a80006000040001d954926e082b04a0941b820ac1282503157f974000ca864330e222222eca8655304224230eca877530077004200ef008601df010d",
"860173",
"460173",
"8b0174",
"0b0174",
"0b0174",
"c30175",
}
var structure *FrameDependencyStructure
for _, h := range hexes {
buf, err := hex.DecodeString(h)
if err != nil {
t.Fatal(err)
}
var ddVal DependencyDescriptor
var d = DependencyDescriptorExtension{
Structure: structure,
Descriptor: &ddVal,
}
if _, err := d.Unmarshal(buf); err != nil {
t.Fatal(err)
}
if ddVal.AttachedStructure != nil {
structure = ddVal.AttachedStructure
}
t.Log(ddVal.String())
}
}
@@ -0,0 +1,446 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"errors"
)
var (
ErrDDReaderNoStructure = errors.New("DependencyDescriptorReader: Structure is nil")
ErrDDReaderTemplateWithoutStructure = errors.New("DependencyDescriptorReader: has templateDependencyStructurePresentFlag but AttachedStructure is nil")
ErrDDReaderTooManyTemplates = errors.New("DependencyDescriptorReader: too many templates")
ErrDDReaderTooManyTemporalLayers = errors.New("DependencyDescriptorReader: too many temporal layers")
ErrDDReaderTooManySpatialLayers = errors.New("DependencyDescriptorReader: too many spatial layers")
ErrDDReaderInvalidTemplateIndex = errors.New("DependencyDescriptorReader: invalid template index")
ErrDDReaderInvalidSpatialLayer = errors.New("DependencyDescriptorReader: invalid spatial layer, should be less than the number of resolutions")
ErrDDReaderNumDTIMismatch = errors.New("DependencyDescriptorReader: decode target indications length mismatch with structure num decode targets")
ErrDDReaderNumChainDiffsMismatch = errors.New("DependencyDescriptorReader: chain diffs length mismatch with structure num chains")
)
type DependencyDescriptorReader struct {
// Output.
descriptor *DependencyDescriptor
// Values that are needed while reading the descriptor, but can be discarded
// when reading is complete.
buffer *BitStreamReader
frameDependencyTemplateId int
activeDecodeTargetsPresentFlag bool
customDtisFlag bool
customFdiffsFlag bool
customChainsFlag bool
structure *FrameDependencyStructure
}
func NewDependencyDescriptorReader(buf []byte, structure *FrameDependencyStructure, descriptor *DependencyDescriptor) *DependencyDescriptorReader {
buffer := NewBitStreamReader(buf)
return &DependencyDescriptorReader{
buffer: buffer,
descriptor: descriptor,
structure: structure,
}
}
func (r *DependencyDescriptorReader) Parse() (int, error) {
if err := r.readMandatoryFields(); err != nil {
return 0, err
}
if len(r.buffer.buf) > 3 {
err := r.readExtendedFields()
if err != nil {
return 0, err
}
}
if r.descriptor.AttachedStructure != nil {
r.structure = r.descriptor.AttachedStructure
}
if r.structure == nil {
r.buffer.Invalidate()
return 0, ErrDDReaderNoStructure
}
if r.activeDecodeTargetsPresentFlag {
bitmask, err := r.buffer.ReadBits(r.structure.NumDecodeTargets)
if err != nil {
return 0, err
}
mask := uint32(bitmask)
r.descriptor.ActiveDecodeTargetsBitmask = &mask
}
err := r.readFrameDependencyDefinition()
if err != nil {
return 0, err
}
return r.buffer.BytesRead(), nil
}
func (r *DependencyDescriptorReader) readMandatoryFields() error {
var err error
r.descriptor.FirstPacketInFrame, err = r.buffer.ReadBool()
if err != nil {
return err
}
r.descriptor.LastPacketInFrame, err = r.buffer.ReadBool()
if err != nil {
return err
}
templateID, err := r.buffer.ReadBits(6)
if err != nil {
return err
}
r.frameDependencyTemplateId = int(templateID)
frameNumber, err := r.buffer.ReadBits(16)
if err != nil {
return err
}
r.descriptor.FrameNumber = uint16(frameNumber)
return nil
}
func (r *DependencyDescriptorReader) readExtendedFields() error {
templateDependencyStructurePresentFlag, err := r.buffer.ReadBool()
if err != nil {
return err
}
flag, err := r.buffer.ReadBool()
if err != nil {
return err
}
r.activeDecodeTargetsPresentFlag = flag
flag, err = r.buffer.ReadBool()
if err != nil {
return err
}
r.customDtisFlag = flag
flag, err = r.buffer.ReadBool()
if err != nil {
return err
}
r.customFdiffsFlag = flag
flag, err = r.buffer.ReadBool()
if err != nil {
return err
}
r.customChainsFlag = flag
if templateDependencyStructurePresentFlag {
err = r.readTemplateDependencyStructure()
if err != nil {
return err
}
if r.descriptor.AttachedStructure == nil {
return ErrDDReaderTemplateWithoutStructure
}
bitmask := uint32((uint64(1) << r.descriptor.AttachedStructure.NumDecodeTargets) - 1)
r.descriptor.ActiveDecodeTargetsBitmask = &bitmask
}
return nil
}
func (r *DependencyDescriptorReader) readTemplateDependencyStructure() error {
r.descriptor.AttachedStructure = &FrameDependencyStructure{}
structureId, err := r.buffer.ReadBits(6)
if err != nil {
return err
}
r.descriptor.AttachedStructure.StructureId = int(structureId)
numDecodeTargets, err := r.buffer.ReadBits(5)
if err != nil {
return err
}
r.descriptor.AttachedStructure.NumDecodeTargets = int(numDecodeTargets) + 1
if err = r.readTemplateLayers(); err != nil {
return err
}
if err = r.readTemplateDtis(); err != nil {
return err
}
if err = r.readTemplateFdiffs(); err != nil {
return err
}
if err = r.readTemplateChains(); err != nil {
return err
}
flag, err := r.buffer.ReadBool()
if err != nil {
return err
}
if flag {
return r.readResolutions()
}
return nil
}
type nextLayerIdcType int
const (
sameLayer nextLayerIdcType = iota
nextTemporalLayer
nextSpatialLayer
noMoreLayer
invalidLayer
)
func (r *DependencyDescriptorReader) readTemplateLayers() error {
var (
templates []*FrameDependencyTemplate
temporalId, spatialId int
nextLayerIdc nextLayerIdcType
)
for {
if len(templates) == MaxTemplates {
return ErrDDReaderTooManyTemplates
}
var lastTemplate FrameDependencyTemplate
templates = append(templates, &lastTemplate)
lastTemplate.TemporalId = temporalId
lastTemplate.SpatialId = spatialId
idc, err := r.buffer.ReadBits(2)
if err != nil {
return err
}
nextLayerIdc = nextLayerIdcType(idc)
if nextLayerIdc == nextTemporalLayer {
temporalId++
if temporalId >= MaxTemporalIds {
return ErrDDReaderTooManyTemporalLayers
}
} else if nextLayerIdc == nextSpatialLayer {
spatialId++
temporalId = 0
if spatialId >= MaxSpatialIds {
return ErrDDReaderTooManySpatialLayers
}
}
if !(nextLayerIdc != noMoreLayer && r.buffer.Ok()) {
break
}
}
r.descriptor.AttachedStructure.Templates = templates
return nil
}
func (r *DependencyDescriptorReader) readTemplateDtis() error {
structure := r.descriptor.AttachedStructure
for _, template := range structure.Templates {
if len(template.DecodeTargetIndications) < structure.NumDecodeTargets {
template.DecodeTargetIndications = append(template.DecodeTargetIndications, make([]DecodeTargetIndication, structure.NumDecodeTargets-len(template.DecodeTargetIndications))...)
} else {
template.DecodeTargetIndications = template.DecodeTargetIndications[:structure.NumDecodeTargets]
}
for i := range template.DecodeTargetIndications {
indication, err := r.buffer.ReadBits(2)
if err != nil {
return err
}
template.DecodeTargetIndications[i] = DecodeTargetIndication(indication)
}
}
return nil
}
func (r *DependencyDescriptorReader) readTemplateFdiffs() error {
for _, template := range r.descriptor.AttachedStructure.Templates {
for {
fdiffFollow, err := r.buffer.ReadBool()
if err != nil {
return err
}
if !fdiffFollow {
break
}
fDiffMinusOne, err := r.buffer.ReadBits(4)
if err != nil {
return err
}
template.FrameDiffs = append(template.FrameDiffs, int(fDiffMinusOne+1))
}
}
return nil
}
func (r *DependencyDescriptorReader) readTemplateChains() error {
structure := r.descriptor.AttachedStructure
numChains, err := r.buffer.ReadNonSymmetric(uint32(structure.NumDecodeTargets) + 1)
if err != nil {
return err
}
structure.NumChains = int(numChains)
if structure.NumChains == 0 {
return nil
}
for i := 0; i < structure.NumDecodeTargets; i++ {
protectedByChain, err := r.buffer.ReadNonSymmetric(uint32(structure.NumChains))
if err != nil {
return err
}
structure.DecodeTargetProtectedByChain = append(structure.DecodeTargetProtectedByChain, int(protectedByChain))
}
for _, frameTemplate := range structure.Templates {
for chainId := 0; chainId < structure.NumChains; chainId++ {
chainDiff, err := r.buffer.ReadBits(4)
if err != nil {
return err
}
frameTemplate.ChainDiffs = append(frameTemplate.ChainDiffs, int(chainDiff))
}
}
return nil
}
func (r *DependencyDescriptorReader) readResolutions() error {
structure := r.descriptor.AttachedStructure
// The way templates are bitpacked, they are always ordered by spatial_id.
spatialLayers := structure.Templates[len(structure.Templates)-1].SpatialId + 1
for sid := 0; sid < spatialLayers; sid++ {
widthMinus1, err := r.buffer.ReadBits(16)
if err != nil {
return err
}
heightMinus1, err := r.buffer.ReadBits(16)
if err != nil {
return err
}
structure.Resolutions = append(structure.Resolutions, RenderResolution{
Width: int(widthMinus1 + 1),
Height: int(heightMinus1 + 1),
})
}
return nil
}
func (r *DependencyDescriptorReader) readFrameDependencyDefinition() error {
templateIndex := (r.frameDependencyTemplateId + MaxTemplates - r.structure.StructureId) % MaxTemplates
if templateIndex >= len(r.structure.Templates) {
r.buffer.Invalidate()
return ErrDDReaderInvalidTemplateIndex
}
// Copy all the fields from the matching template
r.descriptor.FrameDependencies = r.structure.Templates[templateIndex].Clone()
if r.customDtisFlag {
err := r.readFrameDtis()
if err != nil {
return err
}
}
if r.customFdiffsFlag {
err := r.readFrameFdiffs()
if err != nil {
return err
}
}
if r.customChainsFlag {
err := r.readFrameChains()
if err != nil {
return err
}
}
if len(r.structure.Resolutions) == 0 {
r.descriptor.Resolution = nil
} else {
// Format guarantees that if there were resolutions in the last structure,
// then each spatial layer got one.
if r.descriptor.FrameDependencies.SpatialId >= len(r.structure.Resolutions) {
r.buffer.Invalidate()
return ErrDDReaderInvalidSpatialLayer
}
res := r.structure.Resolutions[r.descriptor.FrameDependencies.SpatialId]
r.descriptor.Resolution = &res
}
return nil
}
func (r *DependencyDescriptorReader) readFrameDtis() error {
if len(r.descriptor.FrameDependencies.DecodeTargetIndications) != r.structure.NumDecodeTargets {
return ErrDDReaderNumDTIMismatch
}
for i := range r.descriptor.FrameDependencies.DecodeTargetIndications {
indication, err := r.buffer.ReadBits(2)
if err != nil {
return err
}
r.descriptor.FrameDependencies.DecodeTargetIndications[i] = DecodeTargetIndication(indication)
}
return nil
}
func (r *DependencyDescriptorReader) readFrameFdiffs() error {
r.descriptor.FrameDependencies.FrameDiffs = r.descriptor.FrameDependencies.FrameDiffs[:0]
for {
nexFdiffSize, err := r.buffer.ReadBits(2)
if err != nil {
return err
}
if nexFdiffSize == 0 {
break
}
fDiffMinusOne, err := r.buffer.ReadBits(int(nexFdiffSize * 4))
if err != nil {
return err
}
r.descriptor.FrameDependencies.FrameDiffs = append(r.descriptor.FrameDependencies.FrameDiffs, int(fDiffMinusOne+1))
}
return nil
}
func (r *DependencyDescriptorReader) readFrameChains() error {
if len(r.descriptor.FrameDependencies.ChainDiffs) != r.structure.NumChains {
return ErrDDReaderNumChainDiffsMismatch
}
for i := range r.descriptor.FrameDependencies.ChainDiffs {
chainDiff, err := r.buffer.ReadBits(8)
if err != nil {
return err
}
r.descriptor.FrameDependencies.ChainDiffs[i] = int(chainDiff)
}
return nil
}
@@ -0,0 +1,501 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dependencydescriptor
import (
"fmt"
"golang.org/x/exp/slices"
)
type TemplateMatch struct {
TemplateIdx int
NeedCustomDtis bool
NeedCustomFdiffs bool
NeedCustomChains bool
// Size in bits to store frame-specific details, i.e.
// excluding mandatory fields and template dependency structure.
ExtraSizeBits int
}
type DependencyDescriptorWriter struct {
descriptor *DependencyDescriptor
structure *FrameDependencyStructure
activeChains uint32
writer *BitStreamWriter
bestTemplate TemplateMatch
}
func NewDependencyDescriptorWriter(buf []byte, structure *FrameDependencyStructure, activeChains uint32, descriptor *DependencyDescriptor) (*DependencyDescriptorWriter, error) {
writer := NewBitStreamWriter(buf)
w := &DependencyDescriptorWriter{
descriptor: descriptor,
structure: structure,
activeChains: activeChains,
writer: writer,
}
return w, w.findBestTemplate()
}
func (w *DependencyDescriptorWriter) ResetBuf(buf []byte) {
w.writer = NewBitStreamWriter(buf)
}
func (w *DependencyDescriptorWriter) Write() error {
if err := w.findBestTemplate(); err != nil {
return err
}
if err := w.writeMandatoryFields(); err != nil {
return err
}
if w.hasExtendedFields() {
if err := w.writeExtendedFields(); err != nil {
return err
}
if err := w.writeFrameDependencyDefinition(); err != nil {
return err
}
}
remainingBits := w.writer.RemainingBits()
// Zero remaining memory to avoid leaving it uninitialized.
if remainingBits%64 != 0 {
if err := w.writeBits(0, remainingBits%64); err != nil {
return err
}
}
for i := 0; i < remainingBits/64; i++ {
if err := w.writeBits(0, 64); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) findBestTemplate() error {
// Find templates with same spatial and temporal layer of frame dependency.
var (
firstSameLayer *FrameDependencyTemplate
firstSameLayerIdx, lastSameLayerIdx int
)
for i, t := range w.structure.Templates {
if w.descriptor.FrameDependencies.SpatialId == t.SpatialId &&
w.descriptor.FrameDependencies.TemporalId == t.TemporalId {
firstSameLayer = t
firstSameLayerIdx = i
break
}
}
if firstSameLayer == nil {
return fmt.Errorf("no template found for spatial layer %d and temporal layer %d", w.descriptor.FrameDependencies.SpatialId, w.descriptor.FrameDependencies.TemporalId)
}
for i, t := range w.structure.Templates[firstSameLayerIdx:] {
if w.descriptor.FrameDependencies.SpatialId != t.SpatialId ||
w.descriptor.FrameDependencies.TemporalId != t.TemporalId {
lastSameLayerIdx = i + firstSameLayerIdx
}
}
// Search if there any better template that have small extra size.
w.bestTemplate = w.calculateMatch(firstSameLayerIdx, firstSameLayer)
for i := firstSameLayerIdx + 1; i <= lastSameLayerIdx; i++ {
t := w.structure.Templates[i]
match := w.calculateMatch(i, t)
if match.ExtraSizeBits < w.bestTemplate.ExtraSizeBits {
w.bestTemplate = match
}
}
return nil
}
func (w *DependencyDescriptorWriter) calculateMatch(idx int, template *FrameDependencyTemplate) TemplateMatch {
var result TemplateMatch
result.TemplateIdx = idx
result.NeedCustomFdiffs = w.descriptor.FrameDependencies.FrameDiffs != nil && !slices.Equal(w.descriptor.FrameDependencies.FrameDiffs, template.FrameDiffs)
result.NeedCustomDtis = w.descriptor.FrameDependencies.DecodeTargetIndications != nil && !slices.Equal(w.descriptor.FrameDependencies.DecodeTargetIndications, template.DecodeTargetIndications)
for i := 0; i < w.structure.NumChains; i++ {
if w.activeChains&(1<<i) != 0 && (len(w.descriptor.FrameDependencies.ChainDiffs) <= i || len(template.ChainDiffs) <= i || w.descriptor.FrameDependencies.ChainDiffs[i] != template.ChainDiffs[i]) {
result.NeedCustomChains = true
break
}
}
if result.NeedCustomFdiffs {
result.ExtraSizeBits = 2 * (1 + len(w.descriptor.FrameDependencies.FrameDiffs))
for _, fdiff := range w.descriptor.FrameDependencies.FrameDiffs {
if fdiff <= (1 << 4) {
result.ExtraSizeBits += 4
} else if fdiff <= (1 << 8) {
result.ExtraSizeBits += 8
} else {
result.ExtraSizeBits += 12
}
}
}
if result.NeedCustomDtis {
result.ExtraSizeBits += 2 * len(w.descriptor.FrameDependencies.DecodeTargetIndications)
}
if result.NeedCustomChains {
result.ExtraSizeBits += 8 * w.structure.NumChains
}
return result
}
func (w *DependencyDescriptorWriter) writeMandatoryFields() error {
if err := w.writeBool(w.descriptor.FirstPacketInFrame); err != nil {
return err
}
if err := w.writeBool(w.descriptor.LastPacketInFrame); err != nil {
return err
}
templateId := (w.bestTemplate.TemplateIdx + w.structure.StructureId) % MaxTemplates
if err := w.writeBits(uint64(templateId), 6); err != nil {
return err
}
return w.writeBits(uint64(w.descriptor.FrameNumber), 16)
}
func (w *DependencyDescriptorWriter) writeBool(val bool) error {
v := uint64(0)
if val {
v = 1
}
return w.writer.WriteBits(v, 1)
}
func (w *DependencyDescriptorWriter) writeBits(val uint64, bitCount int) error {
if err := w.writer.WriteBits(val, bitCount); err != nil {
return err
}
return nil
}
func (w *DependencyDescriptorWriter) hasExtendedFields() bool {
return w.bestTemplate.ExtraSizeBits > 0 || w.descriptor.AttachedStructure != nil || w.descriptor.ActiveDecodeTargetsBitmask != nil
}
func (w *DependencyDescriptorWriter) writeExtendedFields() error {
// template_dependency_structure_present_flag
if err := w.writeBool(w.descriptor.AttachedStructure != nil); err != nil {
return err
}
// active_decode_targets_present_flag
activeDecodeTargetsPresentFlag := w.shouldWriteActiveDecodeTargetsBitmask()
if err := w.writeBool(activeDecodeTargetsPresentFlag); err != nil {
return err
}
// need_custom_dtis
if err := w.writeBool(w.bestTemplate.NeedCustomDtis); err != nil {
return err
}
// need_custom_fdiffs
if err := w.writeBool(w.bestTemplate.NeedCustomFdiffs); err != nil {
return err
}
// need_custom_chains
if err := w.writeBool(w.bestTemplate.NeedCustomChains); err != nil {
return err
}
// template_dependency_structure
if w.descriptor.AttachedStructure != nil {
if err := w.writeTemplateDependencyStructure(); err != nil {
return err
}
}
// active_decode_targets_bitmask
if activeDecodeTargetsPresentFlag {
if err := w.writeBits(uint64(*w.descriptor.ActiveDecodeTargetsBitmask), w.structure.NumDecodeTargets); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeTemplateDependencyStructure() error {
if !(w.structure.StructureId >= 0 && w.structure.StructureId < MaxTemplates &&
w.structure.NumDecodeTargets > 0 && w.structure.NumDecodeTargets <= MaxDecodeTargets) {
return fmt.Errorf("invalid arguments, structureId: %d, numDecodeTargets: %d", w.structure.StructureId, w.structure.NumDecodeTargets)
}
if err := w.writeBits(uint64(w.structure.StructureId), 6); err != nil {
return err
}
if err := w.writeBits(uint64(w.structure.NumDecodeTargets-1), 5); err != nil {
return err
}
if err := w.writeTemplateLayers(); err != nil {
return err
}
if err := w.writeTemplateDtis(); err != nil {
return err
}
if err := w.writeTemplateFdiffs(); err != nil {
return err
}
if err := w.writeTemplateChains(); err != nil {
return err
}
hasResolutions := len(w.structure.Resolutions) > 0
if err := w.writeBool(hasResolutions); err != nil {
return err
}
return w.writeResolutions()
}
func (w *DependencyDescriptorWriter) writeTemplateLayers() error {
if !(len(w.structure.Templates) > 0 && len(w.structure.Templates) <= MaxTemplates &&
w.structure.Templates[0].SpatialId == 0 && w.structure.Templates[0].TemporalId == 0) {
return fmt.Errorf("invalid templates, len %d, templates[0]: spatialId %d, temporalId %d", len(w.structure.Templates), w.structure.Templates[0].SpatialId, w.structure.Templates[0].TemporalId)
}
for i := 1; i < len(w.structure.Templates); i++ {
nextLayerIdc := getNextLayerIdc(w.structure.Templates[i-1], w.structure.Templates[i])
if nextLayerIdc >= 3 {
return fmt.Errorf("invalid next_layer_idc %d", nextLayerIdc)
}
if err := w.writeBits(uint64(nextLayerIdc), 2); err != nil {
return err
}
}
return w.writeBits(uint64(noMoreLayer), 2)
}
func getNextLayerIdc(prevTemplate, nextTemplate *FrameDependencyTemplate) nextLayerIdcType {
if nextTemplate.SpatialId == prevTemplate.SpatialId && nextTemplate.TemporalId == prevTemplate.TemporalId {
return sameLayer
} else if nextTemplate.SpatialId == prevTemplate.SpatialId && nextTemplate.TemporalId == prevTemplate.TemporalId+1 {
return nextTemporalLayer
} else if nextTemplate.SpatialId == prevTemplate.SpatialId+1 && nextTemplate.TemporalId == 0 {
return nextSpatialLayer
}
return invalidLayer
}
func (w *DependencyDescriptorWriter) writeTemplateDtis() error {
for _, t := range w.structure.Templates {
for _, dti := range t.DecodeTargetIndications {
if err := w.writeBits(uint64(dti), 2); err != nil {
return err
}
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeTemplateFdiffs() error {
for _, t := range w.structure.Templates {
for _, fdiff := range t.FrameDiffs {
if err := w.writeBits(uint64(1<<4)|uint64(fdiff-1), 1+4); err != nil {
return err
}
}
// no more fdiffs for this template
if err := w.writeBits(uint64(0), 1); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeTemplateChains() error {
if err := w.writeNonSymmetric(uint32(w.structure.NumChains), uint32(w.structure.NumDecodeTargets+1)); err != nil {
return err
}
if w.structure.NumChains == 0 {
return nil
}
for _, protectedBy := range w.structure.DecodeTargetProtectedByChain {
if err := w.writeNonSymmetric(uint32(protectedBy), uint32(w.structure.NumChains)); err != nil {
return err
}
}
for _, t := range w.structure.Templates {
for _, chainDiff := range t.ChainDiffs {
if err := w.writeBits(uint64(chainDiff), 4); err != nil {
return err
}
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeNonSymmetric(value, numValues uint32) error {
return w.writer.WriteNonSymmetric(value, numValues)
}
func (w *DependencyDescriptorWriter) writeResolutions() error {
for _, res := range w.structure.Resolutions {
if err := w.writeBits(uint64(res.Width)-1, 16); err != nil {
return err
}
if err := w.writeBits(uint64(res.Height)-1, 16); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeFrameDependencyDefinition() error {
if w.bestTemplate.NeedCustomDtis {
if err := w.writeFrameDtis(); err != nil {
return err
}
}
if w.bestTemplate.NeedCustomFdiffs {
if err := w.writeFrameFdiffs(); err != nil {
return err
}
}
if w.bestTemplate.NeedCustomChains {
if err := w.writeFrameChains(); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeFrameDtis() error {
for _, dti := range w.descriptor.FrameDependencies.DecodeTargetIndications {
if err := w.writeBits(uint64(dti), 2); err != nil {
return err
}
}
return nil
}
func (w *DependencyDescriptorWriter) writeFrameFdiffs() error {
for _, fdiff := range w.descriptor.FrameDependencies.FrameDiffs {
if fdiff <= (1 << 4) {
if err := w.writeBits(uint64(1<<4)|uint64(fdiff-1), 2+4); err != nil {
return err
}
} else if fdiff <= (1 << 8) {
if err := w.writeBits(uint64(2<<8)|uint64(fdiff-1), 2+8); err != nil {
return err
}
} else { // fdiff <= (1<<12)
if err := w.writeBits(uint64(3<<12)|uint64(fdiff-1), 2+12); err != nil {
return err
}
}
}
// no more fdiffs
return w.writeBits(uint64(0), 2)
}
func (w *DependencyDescriptorWriter) writeFrameChains() error {
for i := 0; i < w.structure.NumChains; i++ {
chainDiff := 0
if w.activeChains&(1<<i) != 0 {
chainDiff = w.descriptor.FrameDependencies.ChainDiffs[i]
}
if err := w.writeBits(uint64(chainDiff), 8); err != nil {
return err
}
}
return nil
}
const mandatoryFieldSize = 1 + 1 + 6 + 16
func (w *DependencyDescriptorWriter) ValueSizeBits() int {
valueSizeBits := mandatoryFieldSize + w.bestTemplate.ExtraSizeBits
if w.hasExtendedFields() {
valueSizeBits += 5
if w.descriptor.AttachedStructure != nil {
valueSizeBits += w.structureSizeBits()
}
if w.shouldWriteActiveDecodeTargetsBitmask() {
valueSizeBits += w.structure.NumDecodeTargets
}
}
return valueSizeBits
}
func (w *DependencyDescriptorWriter) shouldWriteActiveDecodeTargetsBitmask() bool {
if w.descriptor.ActiveDecodeTargetsBitmask == nil {
return false
}
allDecodeTargetsBitmask := (uint64(1) << w.structure.NumDecodeTargets) - 1
if w.descriptor.AttachedStructure != nil && uint64(*w.descriptor.ActiveDecodeTargetsBitmask) == allDecodeTargetsBitmask {
return false
}
return true
}
func (w *DependencyDescriptorWriter) structureSizeBits() int {
// template_id offset (6 bits) and number of decode targets (5 bits)
bits := 11
// template layers
bits += 2 * len(w.structure.Templates)
// dtis
bits += 2 * len(w.structure.Templates) * w.structure.NumDecodeTargets
// fdiffs. each templates uses 1 + 5 * sizeof(fdiff) bits.
bits += len(w.structure.Templates)
for _, t := range w.structure.Templates {
bits += 5 * len(t.FrameDiffs)
}
bits += SizeNonSymmetricBits(uint32(w.structure.NumChains), uint32(w.structure.NumDecodeTargets+1))
if w.structure.NumChains > 0 {
for _, protectedBy := range w.structure.DecodeTargetProtectedByChain {
bits += SizeNonSymmetricBits(uint32(protectedBy), uint32(w.structure.NumChains))
}
bits += 4 * len(w.structure.Templates) * w.structure.NumChains
}
// resolutions
bits += 1 + 32*len(w.structure.Resolutions)
return bits
}
@@ -0,0 +1,72 @@
// Copyright 2024 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package playoutdelay
import (
"encoding/binary"
"errors"
)
const (
PlayoutDelayURI = "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"
MaxPlayoutDelayDefault = 10000 // 10s, equal to chrome's default max playout delay
PlayoutDelayMaxValue = 10 * (1<<12 - 1) // max value for playout delay can be represented
playoutDelayExtensionSize = 3
)
var (
errPlayoutDelayOverflow = errors.New("playout delay overflow")
errTooSmall = errors.New("buffer too small")
)
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=2 | MIN delay | MAX delay |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// The wired MIN/MAX delay is in 10ms unit
type PlayOutDelay struct {
Min, Max uint16 // delay in ms
}
func PlayoutDelayFromValue(min, max uint16) PlayOutDelay {
if min > PlayoutDelayMaxValue {
min = PlayoutDelayMaxValue
}
if max > PlayoutDelayMaxValue {
max = PlayoutDelayMaxValue
}
return PlayOutDelay{Min: min, Max: max}
}
func (p PlayOutDelay) Marshal() ([]byte, error) {
min, max := p.Min/10, p.Max/10
if min >= 1<<12 || max >= 1<<12 {
return nil, errPlayoutDelayOverflow
}
return []byte{byte(min >> 4), byte(min<<4) | byte(max>>8), byte(max)}, nil
}
func (p *PlayOutDelay) Unmarshal(rawData []byte) error {
if len(rawData) < playoutDelayExtensionSize {
return errTooSmall
}
p.Min = (binary.BigEndian.Uint16(rawData) >> 4) * 10
p.Max = (binary.BigEndian.Uint16(rawData[1:]) & 0x0FFF) * 10
return nil
}
@@ -0,0 +1,57 @@
// Copyright 2024 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package playoutdelay
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPlayoutDelay(t *testing.T) {
p1 := PlayOutDelay{Min: 100, Max: 200}
b, err := p1.Marshal()
require.NoError(t, err)
require.Len(t, b, playoutDelayExtensionSize)
var p2 PlayOutDelay
err = p2.Unmarshal(b)
require.NoError(t, err)
require.Equal(t, p1, p2)
// overflow
p3 := PlayOutDelay{Min: 100, Max: (1 << 12) * 10}
_, err = p3.Marshal()
require.ErrorIs(t, err, errPlayoutDelayOverflow)
// too small
p4 := PlayOutDelay{}
err = p4.Unmarshal([]byte{0x00, 0x00})
require.ErrorIs(t, err, errTooSmall)
// from value
p5 := PlayoutDelayFromValue(1<<12*10, 1<<12*10+10)
_, err = p5.Marshal()
require.NoError(t, err)
require.Equal(t, uint16((1<<12)-1)*10, p5.Min)
require.Equal(t, uint16((1<<12)-1)*10, p5.Max)
p6 := PlayOutDelay{Min: 100, Max: PlayoutDelayMaxValue}
bytes, err := p6.Marshal()
require.NoError(t, err)
p6Unmarshal := PlayOutDelay{}
err = p6Unmarshal.Unmarshal(bytes)
require.NoError(t, err)
require.Equal(t, p6, p6Unmarshal)
}