package gateway import ( "context" "sync/atomic" "time" ) type Metrics struct { ActiveSessions atomic.Int64 AdmissionRejects atomic.Uint64 MediaDrops atomic.Uint64 ProviderErrors atomic.Uint64 InputRejected atomic.Uint64 } type MetricsSnapshot struct { ActiveSessions int64 AdmissionRejects uint64 MediaDrops uint64 ProviderErrors uint64 InputRejected uint64 } func (m *Metrics) Snapshot() MetricsSnapshot { return MetricsSnapshot{ ActiveSessions: m.ActiveSessions.Load(), AdmissionRejects: m.AdmissionRejects.Load(), MediaDrops: m.MediaDrops.Load(), ProviderErrors: m.ProviderErrors.Load(), InputRejected: m.InputRejected.Load(), } } type Pacer struct { bytesPerSecond int64 last time.Time } func NewPacer(kbps int64) *Pacer { if kbps < 1 { return &Pacer{} } return &Pacer{bytesPerSecond: kbps * 1000 / 8} } func (p *Pacer) Wait(ctx context.Context, bytes int) error { if p.bytesPerSecond < 1 || bytes < 1 { return nil } now := time.Now() if p.last.IsZero() || now.After(p.last) { p.last = now } delay := time.Duration(float64(bytes) / float64(p.bytesPerSecond) * float64(time.Second)) p.last = p.last.Add(delay) if wait := time.Until(p.last); wait > 0 { timer := time.NewTimer(wait) defer timer.Stop() select { case <-ctx.Done(): return ctx.Err() case <-timer.C: } } return nil }