mirror of
https://github.com/livekit/livekit.git
synced 2026-05-29 16:14:10 +00:00
981fb7cac7
* Adding license notices * remove from config
198 lines
5.0 KiB
Go
198 lines
5.0 KiB
Go
// 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 videolayerselector
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
type selectorDecision int
|
|
|
|
const (
|
|
selectorDecisionMissing selectorDecision = iota
|
|
selectorDecisionDropped
|
|
selectorDecisionForwarded
|
|
selectorDecisionUnknown
|
|
)
|
|
|
|
func (s selectorDecision) String() string {
|
|
switch s {
|
|
case selectorDecisionMissing:
|
|
return "MISSING"
|
|
case selectorDecisionDropped:
|
|
return "DROPPED"
|
|
case selectorDecisionForwarded:
|
|
return "FORWARDED"
|
|
case selectorDecisionUnknown:
|
|
return "UNKNOWN"
|
|
default:
|
|
return fmt.Sprintf("%d", int(s))
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
type SelectorDecisionCache struct {
|
|
initialized bool
|
|
base uint64
|
|
last uint64
|
|
masks []uint64
|
|
numEntries uint64
|
|
numNackEntries uint64
|
|
|
|
onExpectEntityChanged map[uint64][]func(entity uint64, decision selectorDecision)
|
|
}
|
|
|
|
func NewSelectorDecisionCache(maxNumElements uint64, numNackEntries uint64) *SelectorDecisionCache {
|
|
numElements := (maxNumElements*2 + 63) / 64
|
|
return &SelectorDecisionCache{
|
|
masks: make([]uint64, numElements),
|
|
numEntries: numElements * 32, // 2 bits per entry
|
|
numNackEntries: numNackEntries,
|
|
onExpectEntityChanged: make(map[uint64][]func(entity uint64, decision selectorDecision)),
|
|
}
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) AddForwarded(entity uint64) {
|
|
s.addEntity(entity, selectorDecisionForwarded)
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) AddDropped(entity uint64) {
|
|
s.addEntity(entity, selectorDecisionDropped)
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) GetDecision(entity uint64) (selectorDecision, error) {
|
|
if !s.initialized || entity < s.base {
|
|
return selectorDecisionMissing, nil
|
|
}
|
|
|
|
if entity > s.last {
|
|
return selectorDecisionUnknown, nil
|
|
}
|
|
|
|
offset := s.last - entity
|
|
if offset >= s.numEntries {
|
|
// asking for something too old
|
|
return selectorDecisionMissing, fmt.Errorf("too old, oldest: %d, asking: %d", s.last-s.numEntries+1, entity)
|
|
}
|
|
|
|
return s.getEntity(entity), nil
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) ExpectDecision(entity uint64, f func(entity uint64, decision selectorDecision)) bool {
|
|
if !s.initialized || entity < s.base {
|
|
return false
|
|
}
|
|
|
|
if entity < s.last {
|
|
offset := s.last - entity
|
|
if offset >= s.numEntries {
|
|
return false // too old
|
|
}
|
|
}
|
|
|
|
s.onExpectEntityChanged[entity] = append(s.onExpectEntityChanged[entity], f)
|
|
return true
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) addEntity(entity uint64, sd selectorDecision) {
|
|
if !s.initialized {
|
|
s.initialized = true
|
|
s.base = entity
|
|
s.last = entity
|
|
s.setEntity(entity, sd)
|
|
return
|
|
}
|
|
|
|
if entity <= s.base {
|
|
// before base, too old
|
|
return
|
|
}
|
|
|
|
if entity <= s.last {
|
|
s.setEntity(entity, sd)
|
|
return
|
|
}
|
|
|
|
for e := s.last + 1; e != entity; e++ {
|
|
s.setEntity(e, selectorDecisionUnknown)
|
|
}
|
|
|
|
// update [last+1-nack, entity-nack) to missing
|
|
missingStart := s.last
|
|
if missingStart > s.numNackEntries+s.base {
|
|
missingStart -= s.numNackEntries
|
|
} else {
|
|
missingStart = s.base
|
|
}
|
|
missingEnd := entity
|
|
if missingEnd > s.numNackEntries+s.base {
|
|
missingEnd -= s.numNackEntries
|
|
} else {
|
|
missingEnd = s.base
|
|
}
|
|
if missingEnd > missingStart {
|
|
for e := missingStart; e != missingEnd; e++ {
|
|
s.setEntityIfUnknown(e, selectorDecisionMissing)
|
|
}
|
|
}
|
|
|
|
s.setEntity(entity, sd)
|
|
s.last = entity
|
|
|
|
for e, fns := range s.onExpectEntityChanged {
|
|
if e+s.numEntries < s.last {
|
|
delete(s.onExpectEntityChanged, e)
|
|
for _, f := range fns {
|
|
f(e, selectorDecisionMissing)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) setEntityIfUnknown(entity uint64, sd selectorDecision) {
|
|
if s.getEntity(entity) == selectorDecisionUnknown {
|
|
s.setEntity(entity, sd)
|
|
}
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) setEntity(entity uint64, sd selectorDecision) {
|
|
index, bitpos := s.getPos(entity)
|
|
s.masks[index] &= ^(0x3 << bitpos) // clear before bitwise OR
|
|
s.masks[index] |= (uint64(sd) & 0x3) << bitpos
|
|
|
|
if sd != selectorDecisionUnknown {
|
|
if fns, ok := s.onExpectEntityChanged[entity]; ok {
|
|
delete(s.onExpectEntityChanged, entity)
|
|
for _, f := range fns {
|
|
f(entity, sd)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) getEntity(entity uint64) selectorDecision {
|
|
index, bitpos := s.getPos(entity)
|
|
return selectorDecision((s.masks[index] >> bitpos) & 0x3)
|
|
}
|
|
|
|
func (s *SelectorDecisionCache) getPos(entity uint64) (int, int) {
|
|
// 2 bits per entity, a uint64 mask can hold 32 entities
|
|
offset := (entity - s.base) % s.numEntries
|
|
return int(offset >> 5), int(offset&0x1F) * 2
|
|
}
|