v2: mpegts.js + single-upstream fan-out hub (no FFmpeg/HLS)

Replaces the FFmpeg -> MediaMTX -> HLS pipeline with a raw MPEG-TS
fan-out architecture:

- Go stream.Hub holds ONE upstream connection per active channel and
  broadcasts the raw TS bytes to all connected browsers via /ts.
  The IPTV provider only ever sees one IP (the server), no matter how
  many viewers are watching (shared single-channel model).
- Frontend uses mpegts.js to play the raw TS in-browser via MSE,
  fixing HEVC/H.265 4K streams that HLS could not package.
- Dropped FFmpeg, MediaMTX, RTMP/RTSP, HLS proxy, playback/ingest
  modes, the OBS source toggle, and FFmpeg health stats.
- UI look preserved (fullscreen video, collapsible sidebar, status
  bar, upstream-down banner, hourly playlist auto-refresh).

Tradeoff: loses iOS Safari (no MSE/mpegts.js); fixes desktop/Android
playback of both H.264 and H.265.
This commit is contained in:
2026-06-12 19:33:06 -07:00
parent 88a189de34
commit 723ae1f1ac
18 changed files with 432 additions and 899 deletions

View File

@@ -9,38 +9,11 @@ type Channel struct {
StreamURL string `json:"stream_url"`
}
// StreamHealth holds real-time FFmpeg progress metrics for diagnosing issues.
// Speed < 1x indicates the upstream IPTV source can't deliver fast enough.
// Drop frames > 0 with speed ~1x suggests local processing issues.
type StreamHealth struct {
Speed string `json:"speed"` // "1.00x" — upstream delivery rate
FPS string `json:"fps"` // frames per second being processed
Bitrate string `json:"bitrate"` // output bitrate
DropFrames string `json:"drop_frames"` // frames dropped by FFmpeg
DupFrames string `json:"dup_frames"` // frames duplicated by FFmpeg
}
// StreamStatus represents the current streaming state.
type StreamStatus struct {
Source string `json:"source"` // "obs" or "iptv"
ChannelName string `json:"channel_name"` // current IPTV channel name (empty if OBS)
Live bool `json:"live"`
Transitioning bool `json:"transitioning"` // true while switching to a new channel
UpstreamDown bool `json:"upstream_down"` // true when no data is detected from the selected upstream
PlaylistVersion int64 `json:"playlist_version"` // increments when playlist reloads
Health *StreamHealth `json:"health,omitempty"`
}
// ProcessInfo represents the status of a managed child process.
type ProcessInfo struct {
Running bool `json:"running"`
PID int `json:"pid"`
Uptime string `json:"uptime"`
Error string `json:"error,omitempty"`
}
// SystemStatus holds status for all managed processes.
type SystemStatus struct {
MediaMTX ProcessInfo `json:"mediamtx"`
FFmpeg ProcessInfo `json:"ffmpeg"`
ChannelName string `json:"channel_name"` // current IPTV channel name
Live bool `json:"live"` // true when a channel is selected
Transitioning bool `json:"transitioning"` // true while switching to a new channel
UpstreamDown bool `json:"upstream_down"` // true when no data is detected from the selected upstream
PlaylistVersion int64 `json:"playlist_version"` // increments when playlist reloads
}