Turn any web page into an RTMP/HLS stream. Chromium renders the page, ffmpeg captures it, MediaMTX publishes it. Built for NOC environments and digital signage.
## How it works
Each stream runs its own isolated stack inside the container:
All processes are managed by Supervisord. The web UI is a Next.js app that controls everything via a REST API.
## Features
- **Stream any URL** — if it loads in a browser, it streams
- **Dashboard with live thumbnails** — captured from the HLS output, refreshable on demand
- **VNC access** — inspect any stream's virtual display from the browser via unified noVNC (single port, token routing)
- **Autologin with CDP detection** — configure credentials per stream; on restart, queries Chrome DevTools Protocol to skip login if the session is still alive
- **Persistent desired state** — streams remember if they were running or stopped and restore automatically on container restart
- **Fully configurable encoding** — resolution, scale, FPS, bitrate, preset, tune, GOP, threads, all per stream
- **Built-in HLS player** — watch any stream in the browser; also serves a standalone embeddable HTML page per stream
## Quick Start
```yaml
# docker-compose.yml
services:
decap-stream:
image:ghcr.io/riguettodev/decap-stream:latest
container_name:decap-stream
restart:unless-stopped
shm_size:"2gb"
security_opt:
- seccomp:unconfined # required for Chromium syscalls
environment:
TZ:America/Sao_Paulo
ports:
- "3000:3000"# Web UI
- "1935:1935"# RTMP input
- "8888:8888"# HLS output
- "6080:6080"# noVNC
volumes:
- decap-stream:/app/data
volumes:
decap-stream:
```
```bash
docker compose up -d
```
Open **http://localhost:3000** and add your first stream.
> `seccomp:unconfined` is required because Chromium uses syscalls blocked by Docker's default seccomp profile.
> `shm_size: 2gb` prevents Chromium from crashing on `/dev/shm` exhaustion under load.
## Ports
| Port | Service |
|------|---------|
| `3000` | Web UI (Next.js) |
| `1935` | RTMP ingest (MediaMTX) |
| `8888` | HLS output (MediaMTX) |
| `6080` | noVNC unified (token-based routing to all streams) |
## RTMP & HLS URLs
Each stream gets a slug ID you define (e.g. `grafana-prod`):