Skip to content

Commit e15ac90

Browse files
author
Datanoise
committed
fix: implement Ogg page alignment for subscribers to eliminate bitstream corruption and CRC errors
1 parent b5776f2 commit e15ac90

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

relay/relay.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"sort"
8+
"strings"
89
"sync"
910
"sync/atomic"
1011
"time"
@@ -69,6 +70,40 @@ func (cb *CircularBuffer) ReadAt(start int64, p []byte) (int, int64, bool) {
6970
return actual, start + int64(actual), skipped
7071
}
7172

73+
// FindNextPageBoundary searches for the next "OggS" magic in the buffer starting from 'start'
74+
func (cb *CircularBuffer) FindNextPageBoundary(start int64) int64 {
75+
cb.mu.RLock()
76+
defer cb.mu.RUnlock()
77+
78+
if start < cb.Head-cb.Size {
79+
start = cb.Head - cb.Size
80+
}
81+
if start >= cb.Head-4 {
82+
return cb.Head
83+
}
84+
85+
for i := start; i < cb.Head-4; {
86+
pos := i % cb.Size
87+
n := int64(4096) // Search window
88+
if i+n > cb.Head {
89+
n = cb.Head - i
90+
}
91+
if pos+n > cb.Size {
92+
n = cb.Size - pos
93+
}
94+
95+
// Look for OggS in this segment
96+
data := cb.Data[pos : pos+n]
97+
for j := 0; j <= len(data)-4; j++ {
98+
if data[j] == 'O' && data[j+1] == 'g' && data[j+2] == 'g' && data[j+3] == 'S' {
99+
return i + int64(j)
100+
}
101+
}
102+
i += n - 3 // Overlap by 3 bytes to catch split magic
103+
}
104+
return start
105+
}
106+
72107
// Stream represents a single mount point (e.g., /stream)
73108
type Stream struct {
74109
MountName string
@@ -93,6 +128,7 @@ type Stream struct {
93128

94129
OggHead []byte // Store Ogg headers for Opus/Ogg streams
95130
OggHeaderOffset int64 // Absolute buffer offset where headers end
131+
LastPageOffset int64 // Absolute offset of the last valid Ogg page start
96132

97133
Buffer *CircularBuffer
98134
listeners map[string]chan struct{} // Signal channel for new data
@@ -239,6 +275,16 @@ func (s *Stream) Broadcast(data []byte, relay *Relay) {
239275
atomic.AddInt64(&relay.BytesIn, int64(len(data)))
240276
atomic.AddInt64(&s.BytesIn, int64(len(data)))
241277

278+
// Track Ogg Page boundaries for alignment
279+
isOgg := strings.Contains(strings.ToLower(s.ContentType), "ogg") || strings.Contains(strings.ToLower(s.ContentType), "opus")
280+
if isOgg {
281+
for i := 0; i <= len(data)-4; i++ {
282+
if data[i] == 'O' && data[i+1] == 'g' && data[i+2] == 'g' && data[i+3] == 'S' {
283+
s.LastPageOffset = s.Buffer.Head + int64(i)
284+
}
285+
}
286+
}
287+
242288
// 1. Write to shared buffer
243289
s.Buffer.Write(data)
244290

@@ -270,6 +316,13 @@ func (s *Stream) Subscribe(id string, burstSize int) (int64, chan struct{}) {
270316
if start < 0 {
271317
start = 0
272318
}
319+
320+
// For Ogg/Opus, align to the last known page boundary if we are within the burst
321+
if strings.Contains(strings.ToLower(s.ContentType), "ogg") || strings.Contains(strings.ToLower(s.ContentType), "opus") {
322+
if s.LastPageOffset > start {
323+
start = s.LastPageOffset
324+
}
325+
}
273326

274327
// Ensure we don't go back further than the buffer allows
275328
if s.Buffer.Head-start > s.Buffer.Size {

0 commit comments

Comments
 (0)