From 2e66b8c73d814a764591e8f0d0b0e69271878580 Mon Sep 17 00:00:00 2001 From: Scott Register Date: Sat, 7 Feb 2026 09:23:31 -0800 Subject: [PATCH] Add project plan Co-Authored-By: Claude Opus 4.6 --- plan.md | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 plan.md diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..e611854 --- /dev/null +++ b/plan.md @@ -0,0 +1,310 @@ +# Restreamer — Project Plan + +## Overview + +Restreamer is a self-hosted, single-container streaming relay that presents a unified web video player to viewers. It supports two ingest sources: + +1. **OBS/RTMP ingest** — streamers push RTMP directly to the server (like Twitch/Owncast) +2. **IPTV relay** — an admin selects a channel from an M3U playlist, and the server fetches the upstream HLS stream and rebroadcasts it to all viewers through a single connection to the provider + +All viewers connect to the same output stream regardless of the source. The admin panel controls which source is active and, for IPTV mode, which channel is playing. + +**Deployment target:** VM or LXC container on Proxmox, on rented bare-metal in a datacenter. + +--- + +## Architecture + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Docker Container (entrypoint: Go backend) │ +│ │ +│ ┌──────────┐ RTMP ┌───────────┐ │ +│ │ OBS/RTMP │────────────▶│ MediaMTX │──── HLS/WebRTC ──┐ │ +│ │ Streamer │ │ (video │ │ │ +│ └──────────┘ │ engine) │ │ │ +│ └─────▲─────┘ │ │ +│ │ RTMP push │ │ +│ ┌──────────┐ pulls HLS ┌─────┴─────┐ │ │ +│ │ Upstream │◀────────────│ FFmpeg │ │ │ +│ │ IPTV │ │ (remux │ │ │ +│ │ Provider │ │ relay) │ │ │ +│ └──────────┘ └───────────┘ │ │ +│ ▲ ▲ │ │ +│ spawn/kill │ │ spawn/restart │ │ +│ FFmpeg │ │ MediaMTX │ │ +│ ┌─┴───────┴──┐ │ │ +│ │ Go Backend │ │ │ +│ │ (API + │ │ │ +│ │ control) │ │ │ +│ └─────┬──────┘ │ │ +│ │ serves │ │ +│ ┌─────▼──────┐ │ │ +│ │ React/Vite │ │ │ +│ │ Frontend │◀─────────────────┘ │ +│ │ (viewer + │ (player fetches │ +│ │ admin) │ HLS stream) │ +│ └────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────┘ +``` + +### Components + +| Component | Role | Technology | +|-----------|------|------------| +| **MediaMTX** | Video engine — RTMP ingest, HLS/LL-HLS/WebRTC output | [mediamtx](https://github.com/bluenviron/mediamtx) binary | +| **FFmpeg** | Pulls upstream IPTV HLS, remuxes and pushes RTMP into MediaMTX | FFmpeg (no transcoding, copy codecs) | +| **Go Backend** | REST API, M3U parsing, process management (MediaMTX + FFmpeg), admin logic, serves frontend | Go (net/http, stdlib) | +| **React Frontend** | Video player (hls.js), admin panel, source/channel selector | React + Vite, hls.js | +| **Process Manager** | Go backend manages MediaMTX and FFmpeg as child processes | Built into Go backend (os/exec) | + +--- + +## Ingest Paths + +### Path 1: OBS / RTMP Direct + +``` +OBS ──RTMP──▶ MediaMTX (rtmp://server:1935/live/stream) + │ + ▼ + Viewers (HLS/WebRTC via MediaMTX) +``` + +- OBS pushes to `rtmp://:1935/live/stream` +- MediaMTX receives and makes it available as HLS at `http://:8888/live/stream` +- The Go backend detects the stream is active (via MediaMTX API or webhook) +- Frontend player connects to the HLS/WebRTC endpoint + +### Path 2: IPTV Relay + +``` +Admin selects channel ──▶ Go Backend parses M3U ──▶ spawns FFmpeg + │ +IPTV Provider ◀──HLS pull──── FFmpeg ──RTMP push──▶ MediaMTX + │ + Viewers (HLS/WebRTC) +``` + +- Admin uploads or the container mounts an M3U playlist +- Admin picks a channel from the parsed playlist in the web UI +- Go backend spawns FFmpeg: + ``` + ffmpeg -i -c copy -f flv rtmp://localhost:1935/live/stream + ``` +- MediaMTX receives the RTMP push and serves it to all viewers +- Only one upstream connection to the IPTV provider regardless of viewer count +- When admin switches channels, Go backend kills the FFmpeg process and spawns a new one + +--- + +## Feature Scope + +### MVP (Phase 1) + +- [ ] **Docker container** with MediaMTX, FFmpeg, Go backend, React frontend +- [ ] **Go backend** + - REST API for admin operations + - M3U playlist parser (EXTINF, group-title, tvg-logo, etc.) + - Process manager for MediaMTX and FFmpeg (spawn, kill, monitor, restart) + - Source switcher (OBS vs IPTV, and which IPTV channel) + - Serves the built React frontend as static files + - Basic health/status endpoint +- [ ] **React frontend** + - Video player page using hls.js + - Admin panel (toggle source, browse/search IPTV channels, select channel) + - Stream status indicator (live/offline, current source, viewer count if available) +- [ ] **MediaMTX configuration** + - RTMP ingest on port 1935 + - HLS output on port 8888 + - Preconfigured paths for the `live/stream` path +- [ ] **M3U playlist support** + - Parse standard M3U/M3U8 IPTV playlists + - Extract channel name, group, logo URL, stream URL + - Search/filter channels in admin UI + - Playlist loaded via bind mount at `/data/playlist.m3u` + +### Phase 2 (Future) + +- [ ] Admin authentication (env var password, then optional user accounts) +- [ ] Viewer authentication (optional shared password) +- [ ] LL-HLS support via MediaMTX configuration +- [ ] WebRTC playback option (MediaMTX supports this natively) +- [ ] Playlist upload via admin UI (vs bind mount only) +- [ ] Multiple concurrent IPTV channels (viewers choose which to watch) +- [ ] EPG/guide data support (XMLTV) +- [ ] Stream recording/DVR +- [ ] Viewer count tracking +- [ ] HTTPS/TLS termination (or document reverse proxy setup) +- [ ] OBS stream key authentication + +--- + +## API Design (Go Backend) + +### Public Endpoints + +| Method | Path | Description | +|--------|------|-------------| +| `GET` | `/` | Serves the React frontend | +| `GET` | `/api/status` | Current stream status (source, channel name, live/offline) | + +### Admin Endpoints + +| Method | Path | Description | +|--------|------|-------------| +| `GET` | `/api/admin/channels` | List all channels from the M3U playlist | +| `GET` | `/api/admin/channels?search=&group=` | Search/filter channels | +| `GET` | `/api/admin/groups` | List all channel groups from the playlist | +| `POST` | `/api/admin/source` | Set active source `{ "source": "iptv" \| "obs" }` | +| `POST` | `/api/admin/channel` | Select IPTV channel `{ "channel_id": "..." }` | +| `POST` | `/api/admin/playlist/reload` | Re-parse the M3U playlist from disk | +| `GET` | `/api/admin/process/status` | Process status for MediaMTX and FFmpeg (running, pid, uptime, errors) | + +--- + +## Directory Structure + +``` +restreamer/ +├── plan.md # This file +├── Dockerfile # Single all-in-one container +├── mediamtx.yml # MediaMTX configuration +│ +├── backend/ # Go backend +│ ├── go.mod +│ ├── go.sum +│ ├── main.go # Entry point, HTTP server +│ ├── api/ # HTTP handlers +│ │ ├── admin.go # Admin endpoints +│ │ ├── status.go # Public status endpoint +│ │ └── middleware.go # CORS, logging, (future: auth) +│ ├── m3u/ # M3U playlist parser +│ │ └── parser.go +│ ├── process/ # Child process management (FFmpeg + MediaMTX) +│ │ ├── ffmpeg.go # FFmpeg spawn/kill/monitor +│ │ └── mediamtx.go # MediaMTX spawn/monitor/restart +│ └── models/ # Shared types +│ └── types.go +│ +├── frontend/ # React + Vite frontend +│ ├── package.json +│ ├── vite.config.ts +│ ├── index.html +│ ├── src/ +│ │ ├── main.tsx +│ │ ├── App.tsx +│ │ ├── components/ +│ │ │ ├── Player.tsx # hls.js video player +│ │ │ ├── AdminPanel.tsx # Admin controls +│ │ │ ├── ChannelList.tsx # IPTV channel browser +│ │ │ └── StatusBar.tsx # Stream status display +│ │ ├── api/ +│ │ │ └── client.ts # API client for Go backend +│ │ └── types/ +│ │ └── index.ts +│ └── public/ +│ +└── data/ # Runtime data (bind mount target) + └── playlist.m3u # IPTV playlist (mounted in) +``` + +--- + +## Docker Setup + +### Single Container Strategy + +- **Base image:** `ubuntu:24.04` or `alpine:3.19` +- **Installed:** MediaMTX binary, FFmpeg, Go backend binary, built React static files +- **Entrypoint:** The Go backend binary — it spawns MediaMTX as a child process and manages its lifecycle +- **The Go backend** serves the React build output as static files (no separate web server needed) + +### Ports + +| Port | Service | Protocol | +|------|---------|----------| +| `1935` | MediaMTX RTMP ingest | RTMP (TCP) | +| `8080` | Go backend (web UI + API) | HTTP | +| `8888` | MediaMTX HLS output | HTTP | +| `8889` | MediaMTX WebRTC (future) | HTTP | + +> Note: In production, ports 8080 and 8888 could be unified behind a reverse proxy. For MVP, they stay separate. + +### Volumes / Bind Mounts + +| Container Path | Purpose | +|----------------|---------| +| `/data/playlist.m3u` | IPTV M3U playlist file | + +### Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `RESTREAMER_PORT` | `8080` | Go backend listen port | +| `MEDIAMTX_RTMP_PORT` | `1935` | RTMP ingest port | +| `MEDIAMTX_HLS_PORT` | `8888` | HLS output port | +| `PLAYLIST_PATH` | `/data/playlist.m3u` | Path to M3U playlist | + +--- + +## Build & Run + +### Development + +```bash +# Backend +cd backend && go run . + +# Frontend +cd frontend && npm install && npm run dev + +# MediaMTX (download binary and run locally) +./mediamtx mediamtx.yml +``` + +### Docker + +```bash +# Build +docker build -t restreamer . + +# Run +docker run -d \ + --name restreamer \ + -p 1935:1935 \ + -p 8080:8080 \ + -p 8888:8888 \ + -v /path/to/playlist.m3u:/data/playlist.m3u:ro \ + restreamer +``` + +--- + +## Key Design Decisions + +1. **MediaMTX as the video engine** — supports RTMP, HLS, LL-HLS, WebRTC, and RTSP. Modular enough to experiment with different delivery methods later without changing the rest of the stack. + +2. **FFmpeg for IPTV relay (passthrough)** — `ffmpeg -c copy` remuxes without transcoding. Minimal CPU usage. The Go backend manages the FFmpeg lifecycle. + +3. **Single stream path** — both OBS and IPTV push to the same MediaMTX path (`live/stream`). Viewers always connect to one URL. Switching sources is seamless from the viewer's perspective. + +4. **Go backend as the orchestrator and entrypoint** — lightweight, single binary, manages both MediaMTX and FFmpeg as child processes, parses M3U, serves the API and static frontend. No external database needed (state is in-memory for MVP). No supervisord — the Go binary is the container entrypoint and owns the full process tree. + +5. **React/Vite frontend embedded in Go binary** — built at Docker build time, served as static files by the Go backend. Single port for the entire web experience. + +6. **No transcoding** — passthrough only. Keeps CPU usage low and simplifies the pipeline. Can be revisited in Phase 2. + +--- + +## Implementation Order + +1. **Project scaffolding** — Go module, React/Vite app, directory structure +2. **M3U parser** — parse playlists, extract channels +3. **Process manager** — spawn/kill/monitor child processes (MediaMTX + FFmpeg) +4. **Go API server** — REST endpoints, serve static files +5. **MediaMTX config** — configure for RTMP ingest + HLS output +6. **React frontend** — player (hls.js), admin panel, channel list +7. **Dockerfile** — multi-stage build, all-in-one container +8. **Integration testing** — end-to-end with a test stream