Page Stream

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.

How It Works

1

Virtual Display

Container launches Xvfb at your requested resolution.

2

Browser

Playwright-controlled Chromium loads the target page.

3

Capture

FFmpeg performs x11grab of the display and encodes with libx264.

4

Stream

Output is pushed via SRT, RTMP, or saved to a local file.

Features

Single-Command Streaming

Containerized streaming of any web page or local HTML file with one command. No GUI, no desktop, no broadcast software.

CSS & JS Injection

Inject custom CSS overrides and JavaScript into the streamed page for styling transformations and interactive behavior.

Multi-Source Compositor

Compose multiple page sources into collage-style layouts using FFmpeg filter graphs. Side-by-side, tiled, or custom arrangements.

📡

SRT & RTMP

Primary support for SRT (Secure Reliable Transport), secondary RTMP/RTMPS, extensible to any FFmpeg output.

🎬

Direct Video File Streaming

Loop pre-recorded video files directly to ingest endpoints without browser overhead. Auto-scales, pads, and normalizes frame rates.

🔌

Reconnect & Recovery

Exponential backoff reconnection for SRT/RTMP failures. Structured JSON health logging for monitoring.

👁

noVNC Debugging

Optional browser-based VNC viewer to interact with the headless Chromium session in real time. Disabled by default.

Production Ready

Automatic system requirements validation, per-container configuration, secret management, and operational runbooks.

🖥

Web Dashboard

Browser-based control panel for managing streams, monitoring health, and adjusting configuration in real time.

Coming Soon

Quick Start

Deploying 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.

bash
# 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
🛡

Resource Auditing

The bootstrapper automatically scans your host machine's physical CPUs, memory, and Colima allocations to ensure optimal, stutter-free video encodes.

Daemon Management

Check active self-hosted runner process statuses, launch listeners in the background, or stop daemons without writing low-level shell commands.

🏛

Department Templater

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.

Examples

Stream a Web Page via SRT

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

RTMP Output

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

CSS & JS Injection

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

Loop a Video File

# 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

Auto-Refresh Every 5 Minutes

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

Local File Output (Testing)

docker run --rm \
  -v $(pwd)/out:/out \
  page-stream:latest \
  --ingest /out/test.ts \
  --format mpegts \
  --url demo/index.html

CLI Reference

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

Architecture

Standard Instances

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

Compositor Pipeline

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) →⎯

Container Stack

┌───────────────────────────────────────────────────────────┐
│  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               │  │
│                            │  └──────────────────────────┘  │
└────────────────────────────┴──────────────────────────────┘

Health & Monitoring

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
}

Exit Codes

CodeMeaning
0Graceful stop
1Unhandled internal error
10SRT/RTMP reconnect attempts exhausted
11Non-retry protocol failure