Просмотр исходного кода

Add buffered duration metric for audio stream stats

tags/v0.9.0
Jan Svabenik 1 месяц назад
Родитель
Сommit
1d20e798d1
3 измененных файлов: 45 добавлений и 12 удалений
  1. +3
    -0
      docs/API.md
  2. +26
    -12
      internal/audio/stream.go
  3. +16
    -0
      internal/audio/stream_test.go

+ 3
- 0
docs/API.md Просмотреть файл

@@ -298,6 +298,7 @@ Requires `--audio-stdin`, `--audio-http`, or another configured stream source to
"available": 12000,
"capacity": 131072,
"buffered": 0.09,
"bufferedDurationSeconds": 0.27,
"written": 890000,
"underruns": 0,
"overflows": 0
@@ -366,6 +367,7 @@ The stream uses a lock-free ring buffer (default: 2 seconds at input rate). Buff
"available": 12000,
"capacity": 131072,
"buffered": 0.09,
"bufferedDurationSeconds": 0.27,
"written": 890000,
"underruns": 0,
"overflows": 0
@@ -376,5 +378,6 @@ The stream uses a lock-free ring buffer (default: 2 seconds at input rate). Buff
- **underruns**: DSP consumed faster than audio arrived (silence inserted)
- **overflows**: Audio arrived faster than DSP consumed (data dropped)
- **buffered**: Fill ratio (0.0 = empty, 1.0 = full)
- **bufferedDurationSeconds**: Approximate seconds of audio queued in the buffer (`available` frames divided by the sample rate)

When no audio is streaming, the transmitter falls back to the configured tone generator or silence.

+ 26
- 12
internal/audio/stream.go Просмотреть файл

@@ -109,24 +109,38 @@ func (s *StreamSource) Buffered() float64 {

// Stats returns diagnostic counters.
func (s *StreamSource) Stats() StreamStats {
available := s.Available()
buffered := 0.0
if s.size > 0 {
buffered = float64(available) / float64(s.size)
}
return StreamStats{
Available: s.Available(),
Capacity: s.size,
Buffered: s.Buffered(),
Written: s.Written.Load(),
Underruns: s.Underruns.Load(),
Overflows: s.Overflows.Load(),
Available: available,
Capacity: s.size,
Buffered: buffered,
BufferedDurationSeconds: s.bufferedDurationSeconds(available),
Written: s.Written.Load(),
Underruns: s.Underruns.Load(),
Overflows: s.Overflows.Load(),
}
}

// StreamStats exposes runtime telemetry for the stream buffer.
type StreamStats struct {
Available int `json:"available"`
Capacity int `json:"capacity"`
Buffered float64 `json:"buffered"`
Written uint64 `json:"written"`
Underruns uint64 `json:"underruns"`
Overflows uint64 `json:"overflows"`
Available int `json:"available"`
Capacity int `json:"capacity"`
Buffered float64 `json:"buffered"`
BufferedDurationSeconds float64 `json:"bufferedDurationSeconds"`
Written uint64 `json:"written"`
Underruns uint64 `json:"underruns"`
Overflows uint64 `json:"overflows"`
}

func (s *StreamSource) bufferedDurationSeconds(available int) float64 {
if s.SampleRate <= 0 {
return 0
}
return float64(available) / float64(s.SampleRate)
}

// --- StreamResampler ---


+ 16
- 0
internal/audio/stream_test.go Просмотреть файл

@@ -205,6 +205,22 @@ func TestStreamSource_ConcurrentSPSC(t *testing.T) {
}
}

func TestStreamSource_StatsBufferedDuration(t *testing.T) {
rate := 48000
s := NewStreamSource(128, rate)
for i := 0; i < 24; i++ {
s.WriteFrame(NewFrame(0, 0))
}
stats := s.Stats()
if stats.BufferedDurationSeconds <= 0 {
t.Fatalf("expected buffered duration > 0, got %.6f", stats.BufferedDurationSeconds)
}
expected := float64(stats.Available) / float64(rate)
if math.Abs(stats.BufferedDurationSeconds-expected) > 1e-9 {
t.Fatalf("buffered duration %.9f != expected %.9f", stats.BufferedDurationSeconds, expected)
}
}

// --- StreamResampler tests ---

func TestStreamResampler_1to1(t *testing.T) {


Загрузка…
Отмена
Сохранить