Headless, disposable web page video streamer for public displays
Load any URL or local HTML in a containerized headless browser, capture it with FFmpeg, and push encoded video to SRT or RTMP ingest endpoints — no broadcast software required.
Container launches Xvfb at your requested resolution.
Playwright-controlled Chromium loads the target page.
FFmpeg performs x11grab of the display and encodes with libx264.
Output is pushed via SRT, RTMP, or saved to a local file.
Containerized streaming of any web page or local HTML file with one command. No GUI, no desktop, no broadcast software.
Inject custom CSS overrides and JavaScript into the streamed page for styling transformations and interactive behavior.
Compose multiple page sources into collage-style layouts using FFmpeg filter graphs. Side-by-side, tiled, or custom arrangements.
Primary support for SRT (Secure Reliable Transport), secondary RTMP/RTMPS, extensible to any FFmpeg output.
Loop pre-recorded video files directly to ingest endpoints without browser overhead. Auto-scales, pads, and normalizes frame rates.
Exponential backoff reconnection for SRT/RTMP failures. Structured JSON health logging for monitoring.
Optional browser-based VNC viewer to interact with the headless Chromium session in real time. Disabled by default.
Automatic system requirements validation, per-container configuration, secret management, and operational runbooks.
Browser-based control panel for managing streams, monitoring health, and adjusting configuration in real time.
Coming SoonDeploying high-fidelity streaming nodes on your local screens has never been simpler. Avoid manual environment configurations and complex compose syntax by utilizing our friendly, automated host utility.
# 1. Clone the core streaming engine
git clone https://github.com/pu-orfe/page-stream.git
cd page-stream
# 2. Run the interactive bootstrapper
./bootstrap-runner.sh
The bootstrapper automatically scans your host machine's physical CPUs, memory, and Colima allocations to ensure optimal, stutter-free video encodes.
Check active self-hosted runner process statuses, launch listeners in the background, or stop daemons without writing low-level shell commands.
Other campus units can instantly generate private Ops Repositories matching their own codes (e.g., CS, Economics), link it to private credentials, and launch custom display layouts automatically.
docker run --rm \
-e WIDTH=1920 -e HEIGHT=1080 \
page-stream:latest \
--ingest srt://example.com:9000?streamid=live.slp \
--url https://your-dashboard.example.com
docker run --rm \
-e WIDTH=1280 -e HEIGHT=720 \
page-stream:latest \
--ingest rtmp://live.example.com/app/streamKey \
--format flv \
--url https://your-page.example.com
docker run --rm \
-v $(pwd)/custom.css:/custom.css \
-v $(pwd)/custom.js:/custom.js \
page-stream:latest \
--ingest srt://host:9000?streamid=demo \
--url https://your-page.example.com \
--inject-css /custom.css \
--inject-js /custom.js
# Place video in ./videos/ directory
cp /path/to/video.mp4 ./videos/input.mp4
docker run --rm \
-v $(pwd)/videos:/videos:ro \
page-stream:latest \
--ingest srt://host:9000?streamid=test \
--video-file /videos/input.mp4 \
--video-loop
docker run --rm \
-e WIDTH=1280 -e HEIGHT=720 \
page-stream:latest \
--ingest srt://host:9000?streamid=live \
--url https://live-data.example.com \
--auto-refresh-seconds 300
docker run --rm \
-v $(pwd)/out:/out \
page-stream:latest \
--ingest /out/test.ts \
--format mpegts \
--url demo/index.html
page-stream --ingest <URI> [options]
Required:
-i, --ingest <uri> Ingest URI (SRT/RTMP/file)
Page Options:
-u, --url <url> Page URL or local file (default: demo)
--width <n> Viewport width (default: 1280)
--height <n> Viewport height (default: 720)
--fps <n> Frames per second (default: 30)
Encoding:
--preset <p> x264 preset (default: veryfast)
--video-bitrate <kbps> Video bitrate (default: 2500k)
--audio-bitrate <kbps> Audio bitrate (default: 128k)
--format <fmt> Container format (default: mpegts)
--extra-ffmpeg <args...> Additional raw FFmpeg arguments
Browser:
--no-headless Show browser window
--no-fullscreen Disable fullscreen/kiosk mode
--no-app-mode Show full browser chrome
--crop-infobar <px> Crop N pixels from top (remove banner)
Refresh & Control:
--refresh-signal <sig> Reload signal (default: SIGHUP)
--graceful-stop-signal <sig> Stop signal (default: SIGTERM)
--auto-refresh-seconds <n> Auto reload interval (0 = disable)
Reconnection:
--reconnect-attempts <n> Max retries, 0 = infinite (default: 0)
--reconnect-initial-delay-ms Initial backoff delay (default: 1000)
--reconnect-max-delay-ms Max backoff delay (default: 15000)
Monitoring:
--health-interval-seconds <n> Health log interval (default: 30)
Injection:
--inject-css <file> Inject CSS from file
--inject-js <file> Inject JavaScript from file
Video File (bypass browser):
--video-file <path> Stream video file directly
--video-loop Loop video continuously
Each standard instance runs independently on the default network, streaming a single full-HD page to its own ingest endpoint.
standard-1 (1920×1080) → SRT ingest standard-2 (1920×1080) → SRT ingest standard-3 (1920×1080) → SRT ingest standard-4 (1920×1080) → SRT ingest
Two half-width sources are composed into a single full-HD stream via an FFmpeg filter graph on an isolated network.
source-left (960×1080) →⎯
→ compositor → SRT ingest
source-right (960×1080) →⎯
┌───────────────────────────────────────────────────────────┐ │ requirements-check │ │ Validates CPU/RAM before other services start │ ├────────────────────────────┼──────────────────────────────┤ │ default network │ compositor_net │ │ ┌────────────────────────┐ │ ┌──────────────────────────┐ │ │ │ standard-1 standard-2 │ │ │ source-left │ │ │ │ standard-3 standard-4 │ │ │ source-right │ │ │ │ srt-test-listener │ │ │ compositor │ │ │ └────────────────────────┘ │ │ srt-ingest │ │ │ │ └──────────────────────────┘ │ └────────────────────────────┴──────────────────────────────┘
Structured JSON health lines are emitted at configurable intervals:
{
"type": "health",
"ts": "2024-01-01T00:00:00.000Z",
"uptimeSec": 30.1,
"ingest": "srt://example.com:9000?streamid=test",
"protocol": "SRT",
"restartAttempt": 0,
"lastFfmpegExitCode": null,
"retrying": false
}
| Code | Meaning |
|---|---|
0 | Graceful stop |
1 | Unhandled internal error |
10 | SRT/RTMP reconnect attempts exhausted |
11 | Non-retry protocol failure |