- Player: match webplayer.online's proven mpegts.js live-sync config (deep 30-90s buffer, 45s target latency, 1.1x catch-up). The upstream delivers in bursts with multi-second gaps (measured up to ~6s); the deep buffer rides over them so 4K HEVC plays smoothly. - Hub: on subscriber buffer overflow, cleanly disconnect the client so it reconnects and resyncs from a TS packet boundary instead of dropping bytes mid-stream (which corrupted alignment / sync_byte). - Enlarge per-client buffer (256 -> 2048 chunks). - Refresh real1.m3u from upstream.
49 lines
1.1 KiB
Go
49 lines
1.1 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
|
|
// HandleTS streams the shared channel's raw MPEG-TS bytes to a viewer.
|
|
// Every connected browser subscribes to the same upstream via the hub, so the
|
|
// server holds only one connection to the IPTV source regardless of how many
|
|
// clients are watching.
|
|
// GET /ts
|
|
func (a *App) HandleTS(w http.ResponseWriter, r *http.Request) {
|
|
flusher, ok := w.(http.Flusher)
|
|
if !ok {
|
|
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "video/mp2t")
|
|
w.Header().Set("Cache-Control", "no-cache, no-store")
|
|
w.Header().Set("Connection", "keep-alive")
|
|
w.WriteHeader(http.StatusOK)
|
|
flusher.Flush()
|
|
|
|
sub, unsub := a.Hub.Subscribe()
|
|
defer unsub()
|
|
|
|
ctx := r.Context()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-sub.Done():
|
|
return
|
|
case <-sub.Overflow():
|
|
// Client fell too far behind; close so it reconnects and resyncs.
|
|
return
|
|
case chunk, ok := <-sub.Chan():
|
|
if !ok {
|
|
return
|
|
}
|
|
if _, err := w.Write(chunk); err != nil {
|
|
return
|
|
}
|
|
flusher.Flush()
|
|
}
|
|
}
|
|
}
|