mirror of
https://github.com/asticode/go-astiencoder.git
synced 2025-12-24 13:57:53 +08:00
318 lines
7.0 KiB
Go
318 lines
7.0 KiB
Go
package astilibav
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/asticode/go-astiencoder"
|
|
)
|
|
|
|
type Delayer interface {
|
|
Apply(t time.Time) time.Time
|
|
Delay() time.Duration
|
|
HandleFrame(delay time.Duration, n astiencoder.Node)
|
|
}
|
|
|
|
type AdaptiveDelayerOptions struct {
|
|
Handler AdaptiveDelayerHandlerOptions
|
|
Minimum time.Duration
|
|
Step time.Duration
|
|
StepsCount uint
|
|
}
|
|
|
|
type AdaptiveDelayerHandlerOptions struct {
|
|
Average *AdaptiveDelayerAverageHandlerOptions
|
|
Lossless *AdaptiveDelayerLosslessHandlerOptions
|
|
}
|
|
|
|
type AdaptiveDelayer struct {
|
|
d time.Duration
|
|
h adaptiveDelayerHandler
|
|
m *sync.Mutex
|
|
minimum time.Duration
|
|
step time.Duration
|
|
stepsCount int
|
|
}
|
|
|
|
type adaptiveDelayerHandler interface {
|
|
handleFrame(delay time.Duration, n astiencoder.Node)
|
|
updateDelay()
|
|
}
|
|
|
|
func NewAdaptiveDelayer(o AdaptiveDelayerOptions) *AdaptiveDelayer {
|
|
// Create delayer
|
|
d := &AdaptiveDelayer{
|
|
d: o.Minimum,
|
|
m: &sync.Mutex{},
|
|
minimum: o.Minimum,
|
|
step: o.Step,
|
|
stepsCount: int(o.StepsCount),
|
|
}
|
|
if d.stepsCount < 1 {
|
|
d.stepsCount = 1
|
|
}
|
|
|
|
// Create handler
|
|
switch {
|
|
case o.Handler.Average != nil:
|
|
d.h = newAdaptiveDelayerAverageHandler(d, *o.Handler.Average)
|
|
case o.Handler.Lossless != nil:
|
|
d.h = newAdaptiveDelayerLosslessHandler(d, *o.Handler.Lossless)
|
|
}
|
|
return d
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) Apply(t time.Time) time.Time {
|
|
// Lock
|
|
d.m.Lock()
|
|
defer d.m.Unlock()
|
|
|
|
// Add delay
|
|
return t.Add(-d.delayUnsafe())
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) Delay() time.Duration {
|
|
// Lock
|
|
d.m.Lock()
|
|
defer d.m.Unlock()
|
|
|
|
// Get delay
|
|
return d.delayUnsafe()
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) delayUnsafe() time.Duration {
|
|
// Make sure to update delay first
|
|
d.updateDelayUnsafe()
|
|
|
|
// Get delay
|
|
return d.d
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) updateDelayUnsafe() {
|
|
if d.h != nil {
|
|
d.h.updateDelay()
|
|
}
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) HandleFrame(delay time.Duration, n astiencoder.Node) {
|
|
// Lock
|
|
d.m.Lock()
|
|
defer d.m.Unlock()
|
|
|
|
// Make sure to update delay first
|
|
d.updateDelayUnsafe()
|
|
|
|
// Handle frame
|
|
if d.h != nil {
|
|
d.h.handleFrame(delay, n)
|
|
}
|
|
}
|
|
|
|
func (d *AdaptiveDelayer) SetMinimum(min time.Duration) {
|
|
d.minimum = min
|
|
}
|
|
|
|
var _ adaptiveDelayerHandler = (*adaptiveDelayerAverageHandler)(nil)
|
|
|
|
type adaptiveDelayerAverageHandler struct {
|
|
b *adaptiveDelayerAverageHandlerBuffer
|
|
d *AdaptiveDelayer
|
|
lookBehind time.Duration
|
|
marginDecrease time.Duration
|
|
marginIncrease time.Duration
|
|
}
|
|
|
|
type adaptiveDelayerAverageHandlerBuffer struct {
|
|
delays map[astiencoder.Node][]time.Duration
|
|
firstAt time.Time
|
|
}
|
|
|
|
func newAdaptiveDelayerAverageHandlerBuffer(n time.Time) *adaptiveDelayerAverageHandlerBuffer {
|
|
return &adaptiveDelayerAverageHandlerBuffer{
|
|
delays: make(map[astiencoder.Node][]time.Duration),
|
|
firstAt: n,
|
|
}
|
|
}
|
|
|
|
type AdaptiveDelayerAverageHandlerOptions struct {
|
|
LookBehind time.Duration
|
|
MarginDecrease time.Duration
|
|
MarginIncrease time.Duration
|
|
}
|
|
|
|
func newAdaptiveDelayerAverageHandler(d *AdaptiveDelayer, o AdaptiveDelayerAverageHandlerOptions) *adaptiveDelayerAverageHandler {
|
|
return &adaptiveDelayerAverageHandler{
|
|
d: d,
|
|
lookBehind: o.LookBehind,
|
|
marginDecrease: o.MarginDecrease,
|
|
marginIncrease: o.MarginIncrease,
|
|
}
|
|
}
|
|
|
|
func (h *adaptiveDelayerAverageHandler) handleFrame(delay time.Duration, n astiencoder.Node) {
|
|
// Make sure to create buffer
|
|
if h.b == nil {
|
|
h.b = newAdaptiveDelayerAverageHandlerBuffer(now())
|
|
}
|
|
|
|
// Add delay
|
|
h.b.delays[n] = append(h.b.delays[n], delay)
|
|
}
|
|
|
|
func (h *adaptiveDelayerAverageHandler) updateDelay() {
|
|
// Nothing to do
|
|
if h.b == nil || time.Since(h.b.firstAt) < h.lookBehind {
|
|
return
|
|
}
|
|
|
|
// Get max average delay
|
|
var maxAverageDelay *time.Duration
|
|
for _, delays := range h.b.delays {
|
|
// No delays
|
|
if len(delays) <= 0 {
|
|
continue
|
|
}
|
|
|
|
// Get average
|
|
var sum time.Duration
|
|
for _, v := range delays {
|
|
sum += v
|
|
}
|
|
avg := sum / time.Duration(len(delays))
|
|
|
|
// Update max average delay
|
|
if maxAverageDelay == nil || *maxAverageDelay < avg {
|
|
maxAverageDelay = &avg
|
|
}
|
|
}
|
|
|
|
// Process max average delay
|
|
if maxAverageDelay != nil {
|
|
// Loop through steps
|
|
for idx := 0; idx < h.d.stepsCount; idx++ {
|
|
// Get step delay
|
|
stepDelay := time.Duration(idx)*h.d.step + h.d.minimum
|
|
|
|
// Same as current delay
|
|
if h.d.d == stepDelay {
|
|
continue
|
|
}
|
|
|
|
// Get boundaries
|
|
var min, max time.Duration
|
|
if stepDelay < h.d.d {
|
|
min = stepDelay - h.d.step - h.marginDecrease
|
|
max = stepDelay - h.marginDecrease
|
|
} else {
|
|
min = stepDelay - h.d.step - h.marginIncrease
|
|
max = stepDelay - h.marginIncrease
|
|
}
|
|
|
|
// Update delay
|
|
if (idx == 0 || *maxAverageDelay >= min) && (idx == h.d.stepsCount-1 || *maxAverageDelay <= max) {
|
|
h.d.d = stepDelay
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset buffer
|
|
h.b = nil
|
|
}
|
|
|
|
var _ adaptiveDelayerHandler = (*adaptiveDelayerLosslessHandler)(nil)
|
|
|
|
type adaptiveDelayerLosslessHandler struct {
|
|
b map[astiencoder.Node][]adaptiveDelayerLosslessHandlerBufferItem
|
|
d *AdaptiveDelayer
|
|
disableDecrease bool
|
|
lookBehind time.Duration
|
|
}
|
|
|
|
type adaptiveDelayerLosslessHandlerBufferItem struct {
|
|
at time.Time
|
|
delay time.Duration
|
|
}
|
|
|
|
type AdaptiveDelayerLosslessHandlerOptions struct {
|
|
DisableDecrease bool
|
|
LookBehind time.Duration
|
|
}
|
|
|
|
func newAdaptiveDelayerLosslessHandler(d *AdaptiveDelayer, o AdaptiveDelayerLosslessHandlerOptions) *adaptiveDelayerLosslessHandler {
|
|
h := &adaptiveDelayerLosslessHandler{
|
|
b: make(map[astiencoder.Node][]adaptiveDelayerLosslessHandlerBufferItem),
|
|
d: d,
|
|
disableDecrease: o.DisableDecrease,
|
|
lookBehind: o.LookBehind,
|
|
}
|
|
if h.lookBehind <= 0 {
|
|
h.lookBehind = time.Second
|
|
}
|
|
return h
|
|
}
|
|
|
|
func (h *adaptiveDelayerLosslessHandler) handleFrame(delay time.Duration, n astiencoder.Node) {
|
|
// Add delay
|
|
h.b[n] = append(h.b[n], adaptiveDelayerLosslessHandlerBufferItem{
|
|
at: now(),
|
|
delay: delay,
|
|
})
|
|
}
|
|
|
|
func (h *adaptiveDelayerLosslessHandler) updateDelay() {
|
|
// Loop through nodes
|
|
var maxDelay time.Duration
|
|
for n := range h.b {
|
|
// Loop through items
|
|
for idx := 0; idx < len(h.b[n]); idx++ {
|
|
// Get item
|
|
i := h.b[n][idx]
|
|
|
|
// Item is too old
|
|
if now().Sub(i.at) >= h.lookBehind {
|
|
h.b[n] = append(h.b[n][:idx], h.b[n][idx+1:]...)
|
|
idx--
|
|
continue
|
|
}
|
|
|
|
// Update max delay
|
|
if i.delay > maxDelay {
|
|
maxDelay = i.delay
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the best step to have no lost frames
|
|
for idx := 0; idx < h.d.stepsCount; idx++ {
|
|
// Get step delay
|
|
stepDelay := time.Duration(idx)*h.d.step + h.d.minimum
|
|
|
|
// Update delay
|
|
if idx == h.d.stepsCount-1 || stepDelay > maxDelay {
|
|
if !h.disableDecrease || h.d.d < stepDelay {
|
|
h.d.d = stepDelay
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
type ConstantDelayer struct {
|
|
delay time.Duration
|
|
}
|
|
|
|
func NewConstantDelayer(delay time.Duration) *ConstantDelayer {
|
|
return &ConstantDelayer{delay: delay}
|
|
}
|
|
|
|
func (d *ConstantDelayer) Apply(t time.Time) time.Time {
|
|
return t.Add(-d.delay)
|
|
}
|
|
|
|
func (d *ConstantDelayer) Delay() time.Duration {
|
|
return d.delay
|
|
}
|
|
|
|
func (d *ConstantDelayer) HandleFrame(delay time.Duration, n astiencoder.Node) {}
|