diff --git a/.github/workflows/site-checks.yml b/.github/workflows/site-checks.yml new file mode 100644 index 0000000..5bc150d --- /dev/null +++ b/.github/workflows/site-checks.yml @@ -0,0 +1,52 @@ +name: Website checks + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + - name: Lint PHP files + run: find . -name '*.php' -print0 | xargs -0 -n1 php -l + + - name: Validate sitemap XML + run: | + sudo apt-get update + sudo apt-get install -y libxml2-utils + xmllint --noout sitemap.xml + + - name: Smoke-test public pages + run: | + php -S 127.0.0.1:8090 >/tmp/openrtmp-site.log 2>&1 & + server_pid=$! + trap 'kill "$server_pid"' EXIT + sleep 2 + for path in \ + / \ + /quickstart/ \ + /guides/ \ + /guides/self-hosted-rtmp-server-docker/ \ + /guides/rtmps-server-obs/ \ + /guides/enhanced-rtmp-hevc-av1-opus/ \ + /guides/openrtmp-vs-nginx-rtmp/ \ + /docs/ \ + /download/ \ + /legal/; do + curl --fail --silent --show-error "http://127.0.0.1:8090${path}" >/dev/null + done diff --git a/README.md b/README.md index 6cae465..e16f69e 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,68 @@ # openrtmp.org -Source for the [OpenRTMP.org](https://openrtmp.org) website — a static-ish PHP/HTML/JS site (no build step, no framework, no database). +Source for [OpenRTMP.org](https://openrtmp.org), the public website for the OpenRTMP ecosystem. + +OpenRTMP includes: + +- [`librtmp2`](https://github.com/OpenRTMP/librtmp2) — Rust RTMP/RTMPS and Enhanced RTMP protocol library with a C-compatible FFI +- [`librtmp2-server`](https://github.com/OpenRTMP/librtmp2-server) — self-hosted RTMP/RTMPS server with SQLite, stream keys, REST API, and statistics +- [`librtmp2-server-panel`](https://github.com/OpenRTMP/librtmp2-server-panel) — web UI for stream lifecycle and live monitoring + +All projects are active alpha software. The website intentionally avoids hard-coded release numbers where a package registry or GitHub release page can remain the source of truth. ## Local development -Requires PHP 7.4+. +The site is a static-ish PHP/HTML/CSS/JavaScript project with no framework, build step, or database. It requires PHP 7.4 or newer. ```bash -php -S localhost:8000 +php -S localhost:8090 ``` -Then open http://localhost:8000. +Open `http://localhost:8090`. -If port `8000` is already in use (e.g. by `librtmp2-server-panel`), pick another port: +Validate PHP files before publishing: ```bash -php -S localhost:8090 +find . -name '*.php' -print0 | xargs -0 -n1 php -l ``` ## Structure -``` -index.php Home page (hero, features, architecture, ecosystem) -docs/index.php Documentation (librtmp2, server, panel, Docker) -download/index.php Download, build, and Docker deployment instructions -legal/index.php Contact / legal notice (provider info only) -includes/ Shared header/footer PHP partials -assets/css/ Stylesheet -assets/js/ Nav toggle, copy-to-clipboard, docs scrollspy -assets/img/ Favicon / logo +```text +index.php Homepage and audience paths +quickstart/index.php Five-minute Docker + OBS setup +docs/index.php Reference documentation +download/index.php Crate, source, and Docker downloads +guides/index.php Guide landing page +guides/*/index.php Search-focused technical guides +legal/index.php Contact and legal notice +includes/ Shared header and footer +assets/css/style.css Core design system +assets/css/content.css Article and quickstart styles +assets/js/ Navigation, copy, and docs behavior +assets/img/ Logo and favicon +robots.txt Crawler policy +sitemap.xml Indexable public pages ``` -## Pages +## Content principles -| Path | Content | -|------|---------| -| `/` | Project overview, code example, layer stack, ecosystem cards | -| `/docs/` | Getting started, callbacks, `librtmp2-server`, panel, Docker | -| `/download/` | Cargo deps, source builds, GHCR Docker images | -| `/legal/` | Service provider and contact information | +- Separate the **developer/library** path from the **operator/server** path. +- State the alpha status and implementation boundaries consistently. +- Link to repository implementation-status tables for code-accurate claims. +- Prefer package registries and release pages over manually copied latest-version strings. +- Create one canonical page for each major search intent instead of duplicating setup text. +- Keep guides task-focused, honest about missing features, and useful without marketing language. -## Context +## Adding a guide -The site documents the OpenRTMP ecosystem: +1. Create `guides//index.php`. +2. Set a unique `$pageTitle`, `$pageDescription`, and `$canonicalPath`. +3. Add `TechArticle` structured data when appropriate. +4. Link the guide from `guides/index.php` and relevant existing pages. +5. Add the canonical URL to `sitemap.xml`. +6. Run PHP lint and review mobile table/code overflow. -- **[librtmp2](https://github.com/OpenRTMP/librtmp2)** — Rust RTMP/E-RTMP protocol library (alpha, currently `v0.3.1`) -- **[librtmp2-server](https://github.com/OpenRTMP/librtmp2-server)** — reference media server with REST API and stats -- **[librtmp2-server-panel](https://github.com/OpenRTMP/librtmp2-server-panel)** — Flask web UI for stream management +## Deployment -All three projects are alpha. The website is English-only. +Deploy the repository contents to a PHP-capable web root. The server should route directory requests such as `/quickstart/` to the corresponding `index.php` and serve XML/TXT/CSS/JS/image files directly. diff --git a/assets/css/content.css b/assets/css/content.css new file mode 100644 index 0000000..26d503a --- /dev/null +++ b/assets/css/content.css @@ -0,0 +1,287 @@ +/* Additional content, quickstart, and guide styles. */ + +.path-card { + display: flex; + flex-direction: column; +} + +.path-card > p { margin-bottom: 18px; } + +.check-list { + list-style: none; + padding: 0; + margin: 20px 0 24px; +} + +.check-list li { + position: relative; + padding-left: 26px; + margin-bottom: 10px; + color: var(--text-dim); +} + +.check-list li::before { + content: "✓"; + position: absolute; + left: 0; + color: var(--green); + font-weight: 700; +} + +.muted-list li::before { + content: "–"; + color: var(--accent-2); +} + +.card-actions { + display: flex; + gap: 10px; + flex-wrap: wrap; + margin-top: auto; + padding-top: 8px; +} + +.center-link { + display: flex; + justify-content: center; + margin-top: 28px; +} + +.status-note { + color: var(--accent-2); + font-size: 0.84rem; +} + +.card-link { + margin-top: 16px !important; + color: var(--accent-2) !important; + font-weight: 600; +} + +.card-link a:hover, +.text-link:hover { color: var(--text); } + +.content-section { padding-top: 36px; } + +.article-hero { max-width: 900px; } + +.article-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 250px; + align-items: start; + gap: 42px; +} + +.prose { + min-width: 0; + max-width: 820px; +} + +.prose h2 { + margin: 54px 0 16px; + padding-top: 12px; + font-size: clamp(1.45rem, 3vw, 2rem); + line-height: 1.25; + letter-spacing: -0.02em; +} + +.prose h2:first-of-type { margin-top: 28px; } + +.prose h3 { + margin: 32px 0 10px; + font-size: 1.16rem; +} + +.prose p, +.prose li { color: var(--text-dim); } + +.prose a { + color: var(--accent-2); + text-decoration: underline; + text-decoration-color: rgba(255, 176, 46, 0.35); + text-underline-offset: 3px; +} + +.prose a.btn { + color: inherit; + text-decoration: none; +} + +.prose ul, +.prose ol { + padding-left: 24px; + margin: 16px 0 24px; +} + +.prose li { margin-bottom: 8px; } + +.prose code { + font-family: var(--mono); + font-size: 0.9em; + color: #ffd7c9; +} + +.prose pre { + margin: 20px 0 26px; + padding: 20px; + overflow-x: auto; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface); + box-shadow: 0 16px 45px -30px rgba(0, 0, 0, 0.8); +} + +.prose pre code { + color: var(--text); + font-size: 0.86rem; + line-height: 1.65; +} + +.prose table { + width: 100%; + margin: 20px 0 30px; + border-collapse: collapse; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; + display: table; +} + +.prose th, +.prose td { + padding: 13px 15px; + border-bottom: 1px solid var(--border); + text-align: left; + vertical-align: top; +} + +.prose th { + color: var(--text); + background: var(--surface-2); + font-size: 0.88rem; +} + +.prose td { + color: var(--text-dim); + font-size: 0.9rem; +} + +.prose tr:last-child td { border-bottom: 0; } + +.callout { + margin: 8px 0 30px; + padding: 18px 20px; + border: 1px solid rgba(61, 220, 151, 0.3); + border-left: 4px solid var(--green); + border-radius: 10px; + background: rgba(61, 220, 151, 0.07); + color: var(--text-dim); +} + +.callout.warning { + border-color: rgba(255, 176, 46, 0.32); + border-left-color: var(--accent-2); + background: rgba(255, 176, 46, 0.07); +} + +.callout strong { color: var(--text); } + +.toc-card { + position: sticky; + top: 92px; + display: flex; + flex-direction: column; + gap: 8px; + padding: 20px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: rgba(19, 26, 36, 0.92); +} + +.toc-card strong { + margin-bottom: 6px; + font-size: 0.86rem; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.toc-card a { + color: var(--text-dim); + font-size: 0.88rem; +} + +.toc-card a:hover { color: var(--accent-2); } + +.faq-item { + margin: 18px 0; + padding: 18px 20px; + border: 1px solid var(--border); + border-radius: 10px; + background: var(--surface); +} + +.faq-item h3 { margin-top: 0; } +.faq-item p { margin-bottom: 0; } + +.compact-cta { + margin-top: 56px; + padding: 42px 26px; +} + +.guide-grid { align-items: stretch; } + +.guide-card { + display: flex; + flex-direction: column; +} + +.guide-card h2 { + margin: 10px 0 12px; + font-size: 1.35rem; + line-height: 1.3; +} + +.guide-card p { margin-bottom: 18px; } + +.guide-card .text-link { + margin-top: auto; + color: var(--accent-2); + font-weight: 600; +} + +.guide-tag { + color: var(--accent-2); + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.comparison-table td:first-child { + color: var(--text); + font-weight: 600; + width: 22%; +} + +@media (max-width: 920px) { + .article-layout { grid-template-columns: 1fr; } + .toc-card { + position: static; + grid-row: 1; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .toc-card strong { grid-column: 1 / -1; } +} + +@media (max-width: 680px) { + .prose table { + display: block; + overflow-x: auto; + } + .toc-card { grid-template-columns: 1fr; } + .card-actions { flex-direction: column; } + .card-actions .btn { justify-content: center; } + .compact-cta { padding: 34px 20px; } +} diff --git a/assets/js/main.js b/assets/js/main.js index b2e23d4..5363bc7 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -14,6 +14,7 @@ function initNavToggle() { toggle.addEventListener('click', () => { nav.classList.toggle('open'); + toggle.setAttribute('aria-expanded', String(nav.classList.contains('open'))); }); } diff --git a/download/index.php b/download/index.php index 38528ed..4851e77 100644 --- a/download/index.php +++ b/download/index.php @@ -1,176 +1,136 @@
-

Download & Build

-

librtmp2 is published on crates.io. librtmp2-server and librtmp2-server-panel are separate repositories with prebuilt Docker images. All three projects are currently Alpha — pin to a specific version and test your workflow before production use.

+ Crate · Source · Docker +

Download and deploy OpenRTMP

+

Choose the protocol library when building an application, or use the published server and panel images for a self-hosted RTMP stack.

-
- -
-

📁 Cargo (recommended for alpha)

-

Add librtmp2 from crates.io. version = "0" accepts any 0.x release — run cargo update -p librtmp2 regularly to pull security fixes and new alpha builds. RTMPS/TLS is on by default (tls feature).

-
[dependencies.librtmp2]
-version = "0"
+      
+ All OpenRTMP projects are active alpha software. Review each repository's implementation status, pin tested versions, and validate your complete workflow before critical production use. +
-# refresh to the newest matching crates.io release: -# cargo update -p librtmp2
-
+
+
+
📡
+

Run server + panel

+

Use the standalone Compose stack for the fastest evaluation. It pulls published images and does not require source checkouts or a Rust toolchain.

+ +
+
+
🛠️
+

Embed the Rust library

+

Add librtmp2 from crates.io for custom RTMP servers, clients, relays, plugins, and protocol tooling.

+ +
+
-
-

🔒 Pinned version

-

Lock to an exact release for reproducible CI or production builds. Latest on crates.io: 0.3.1. Prefer the alpha range above if you want automatic security updates within 0.x.

-
[dependencies.librtmp2]
-version = "=0.3.1"
+
+
+

📦 Install librtmp2 with Cargo

+

Let Cargo select the current release, then commit Cargo.lock for applications. Libraries should choose an explicit compatibility range based on the release they have tested.

+
cargo add librtmp2
+cargo build
+cargo test
+

RTMPS/TLS is enabled by default. To build without the optional OpenSSL-backed TLS feature:

+
cargo add librtmp2 --no-default-features
+

Use the release shown on crates.io as the source of truth instead of copying a version number from this website.

-

⚙️ Build from source

-

Clone and build locally for development or to track upstream fixes closely. Requires Rust 1.93+.

+

⚙️ Build librtmp2 from source

git clone https://github.com/OpenRTMP/librtmp2.git
 cd librtmp2
 cargo build --release
 cargo test
+

The repository documents the code-accurate implementation status for legacy RTMP, E-RTMP v1/v2, client behavior, TLS, and interoperability tests.

-

🔌 librtmp2-server

-

The reference RTMP/E-RTMP media server (Alpha). SQLite persistence, REST API, key-protected stats, optional RTMPS. See the server docs for configuration and API details.

+

🔌 Build librtmp2-server from source

git clone https://github.com/OpenRTMP/librtmp2-server.git
 cd librtmp2-server
 cargo build --release
 cp .env.example .env
 LRTMP2_DB=./server.db ./target/release/librtmp2-server
+

The API token is generated on first startup unless supplied as the real LRTMP2_API_TOKEN process environment variable.

-
-

🐳 Docker: librtmp2-server only

-

Prebuilt multi-arch images (amd64 / arm64 / riscv64) are published to ghcr.io/openrtmp/librtmp2-server on every release. On first start the server generates an API bearer token and prints it once to the logs — copy it for REST API calls or for the panel.

-
docker run -d \
-  --name librtmp2-server \
-  -p 1935:1935 \
-  -p 8080:8080 \
-  # -p 1936:1936   # RTMPS — only when TLS_ENABLED=true
-  -v librtmp2-server-data:/data \
-  ghcr.io/openrtmp/librtmp2-server:latest
+        
+

🐳 Docker Compose: server + panel + Redis

+

The recommended evaluation path uses the standalone Compose file from the panel repository:

+
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
+cd librtmp2-server-panel
 
-docker logs librtmp2-server   # copy the generated API token
-

Or build from source with the repo's docker-compose.yml:

-
git clone https://github.com/OpenRTMP/librtmp2-server.git
-cd librtmp2-server
-docker compose up -d
+# Create .env with LRTMP2_API_TOKEN, PASSWORD, and SECRET_KEY. +docker compose -f compose.quickstart.yml up -d
+

Follow the quickstart for secure secret generation, OBS settings, health checks, troubleshooting, and the production checklist.

-
-

🐳 Docker: librtmp2-server-panel only

-

Prebuilt image: ghcr.io/openrtmp/librtmp2-server-panel. Requires a running server and its API token (docker logs librtmp2-server). Put both containers on the same Docker network so the panel can reach the server by container name.

-
docker network create openrtmp   # skip if it already exists
-
-docker run -d \
-  --name librtmp2-server-panel \
-  --network openrtmp \
-  -p 8000:8000 \
-  -e LRTMP2_API_URL=http://librtmp2-server:8080 \
-  -e LRTMP2_STATS_URL=http://localhost:8080 \
-  -e LRTMP2_API_TOKEN=<token-from-server-logs> \
-  -e LRTMP2_DOMAIN=localhost \
-  -e PASSWORD=<panel-password-12-chars-or-more> \
-  -e SECRET_KEY=<random-32-plus-char-secret> \
-  ghcr.io/openrtmp/librtmp2-server-panel:latest
-
-# Panel: http://localhost:8000
-

If the server runs on the host (not in Docker), use LRTMP2_API_URL=http://host.docker.internal:8080 on Windows/macOS instead of the container name.

+
+

🐳 Server image

+

Published for multiple architectures at:

+
ghcr.io/openrtmp/librtmp2-server
+

Use a release tag for tested deployments. Moving tags such as latest, beta, and alpha are convenient for evaluation but can change underneath an automated redeploy.

-
-

🐳 Docker: server + panel (docker run)

-

Full stack without Compose — shared network, optional Redis for panel rate limiting, one API token for both services. Generate secrets first:

-
export LRTMP2_API_TOKEN=$(openssl rand -hex 32)
-export PANEL_PASSWORD='your-panel-password'
-export PANEL_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))")
-
-docker network create openrtmp
-
-docker run -d \
-  --name librtmp2-panel-redis \
-  --network openrtmp \
-  redis:7-alpine
+        
+

🐳 Panel image

+

Published for multiple architectures at:

+
ghcr.io/openrtmp/librtmp2-server-panel
+

The panel requires a reachable server API, the matching API token, a public RTMP hostname, login credentials, and a Flask session secret.

+
-docker run -d \ +
+

📡 Run the server image by itself

+
docker run -d \
   --name librtmp2-server \
-  --network openrtmp \
   -p 1935:1935 \
   -p 8080:8080 \
-  -e LRTMP2_API_TOKEN=$LRTMP2_API_TOKEN \
-  -e LRTMP2_DB=/data/server.db \
+  -e LRTMP2_API_TOKEN="$(openssl rand -hex 32)" \
   -v librtmp2-server-data:/data \
-  ghcr.io/openrtmp/librtmp2-server:latest
+  ghcr.io/openrtmp/librtmp2-server:latest
+

Save the supplied token before running the command. If you omit it, retrieve the generated token from the first-start logs. For repeatable deployments, pin the image tag.

+
-docker run -d \ +
+

🎨 Run the panel against an existing server

+
docker run -d \
   --name librtmp2-server-panel \
-  --network openrtmp \
   -p 8000:8000 \
-  -e LRTMP2_API_URL=http://librtmp2-server:8080 \
-  -e LRTMP2_STATS_URL=http://localhost:8080 \
-  -e LRTMP2_API_TOKEN=$LRTMP2_API_TOKEN \
-  -e LRTMP2_DOMAIN=localhost \
-  -e PASSWORD=$PANEL_PASSWORD \
-  -e SECRET_KEY=$PANEL_SECRET \
-  -e RATELIMIT_STORAGE_URI=redis://librtmp2-panel-redis:6379/0 \
-  ghcr.io/openrtmp/librtmp2-server-panel:latest
-
-# RTMP:  rtmp://localhost:1935/live
-# API:   http://localhost:8080/api/v1/health
-# Panel: http://localhost:8000
+ -e LRTMP2_API_URL=http://server.internal:8080 \ + -e LRTMP2_STATS_URL=https://api.example.com \ + -e LRTMP2_DOMAIN=stream.example.com \ + -e LRTMP2_API_TOKEN=<matching-server-token> \ + -e USERNAME=admin \ + -e PASSWORD=<strong-password> \ + -e SECRET_KEY=<random-session-secret> \ + ghcr.io/openrtmp/librtmp2-server-panel:latest
- -
-

🐳 Docker: server + panel (docker compose)

-

Same stack via the panel repo's docker-compose.yml (server, panel, Redis). Set secrets in .env before the first start.

-
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
-cd librtmp2-server-panel
-cp .env.example .env
-
-# Edit .env — at minimum set:
-#   LRTMP2_API_TOKEN  (openssl rand -hex 32)
-#   PASSWORD          (panel login, 12+ chars)
-#   SECRET_KEY        (python3 -c "import secrets; print(secrets.token_hex(32))")
-#   LRTMP2_DOMAIN     (public host/IP for RTMP URLs)
-
-docker compose up -d
-

The shared LRTMP2_API_TOKEN is seeded into the server's SQLite database on first startup. See the panel docs for all environment variables.

-
- -
-

🎨 librtmp2-server-panel (source)

-

Flask web UI for stream management. Can connect to an already-running server (Docker or native). Default login is enabled — set PASSWORD and SECRET_KEY in .env.

-
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
-cd librtmp2-server-panel
-cp .env.example .env   # set LRTMP2_API_TOKEN, PASSWORD, etc.
-pip install -r requirements.txt
-python3 app.py
-
-
-
-
-
-
-
-

Need configuration details?

-

RTMPS setup, stream keys, REST API endpoints, and panel environment variables are documented on the docs page.

+
+

Not sure which component you need?

+

The homepage separates the library and operator paths, while the guides cover Docker, OBS, RTMPS, and Enhanced RTMP.

diff --git a/guides/enhanced-rtmp-hevc-av1-opus/index.php b/guides/enhanced-rtmp-hevc-av1-opus/index.php new file mode 100644 index 0000000..8ba28d9 --- /dev/null +++ b/guides/enhanced-rtmp-hevc-av1-opus/index.php @@ -0,0 +1,116 @@ + 'https://schema.org', + '@type' => 'TechArticle', + 'headline' => 'Enhanced RTMP with HEVC, AV1, and Opus', + 'description' => $pageDescription, + 'author' => ['@type' => 'Organization', 'name' => 'OpenRTMP'], + 'mainEntityOfPage' => 'https://openrtmp.org/guides/enhanced-rtmp-hevc-av1-opus/' +]; +include __DIR__ . '/../../includes/header.php'; +?> + +
+
+ E-RTMP · HEVC · AV1 · Opus +

Enhanced RTMP codecs explained

+

Enhanced RTMP extends the media signaling used by classic RTMP/FLV so modern codecs and richer capabilities can travel through familiar RTMP publishing workflows.

+
+ +
+
+
+
Implementation note: codec passthrough, parser modules, and full session-level negotiation are different things. Always check the current implementation status before depending on a feature.
+ +

Why legacy RTMP needed an extension

+

Classic RTMP commonly carries media using FLV-era codec identifiers. That model works well for established H.264/AAC workflows but does not naturally describe newer codecs such as HEVC, AV1, or Opus.

+

Enhanced RTMP introduces extended audio/video signaling and FourCC-based codec identification. This allows a sender and receiver to distinguish modern codec payloads without pretending they are legacy FLV codecs.

+ +

Key concepts

+

Enhanced video and audio packets

+

Extended packet forms carry information beyond the small legacy codec identifier space. Implementations must parse the extended header correctly before interpreting codec-specific payloads.

+

FourCC codec identifiers

+

Four-character codes identify codecs such as HEVC or AV1 in a way that is more extensible than the original FLV enumeration.

+

Capability negotiation

+

Enhanced RTMP v2 adds structures for advertising and negotiating supported capabilities. Having parser code for these structures is not the same as completing every negotiation path in a production session state machine.

+

Multitrack and ModEx

+

Later extensions describe multiple media tracks and modular metadata. Applications must decide how those tracks map into their own routing, playback, recording, and statistics models.

+ +

How OpenRTMP handles Enhanced RTMP

+

OpenRTMP separates several layers:

+
    +
  • Wire parser/serializer modules: reusable structures for enhanced media and protocol extensions.
  • +
  • Default live session path: the code actually used when publishers and players connect through the library.
  • +
  • Server application layer: authentication, routing, stream registry, statistics, and API behavior built around the library.
  • +
+

Current OpenRTMP workflows can relay enhanced media payloads from compatible OBS/FFmpeg publishers. Some advanced v2 features may exist as library code and tests without being fully integrated into every default live-session path. The repository's status table is the source of truth.

+ +

Codec expectations

+ + + + + + + + +
CodecWhat to verify
H.264 + AACLegacy initialization frames, publisher/player compatibility, and late-join playback
HEVCEnhanced video signaling, sequence-start handling, player support, and actual decoder availability
AV1Sender support, FourCC signaling, decoder support, and client CPU/GPU requirements
OpusEnhanced audio signaling and whether the receiving player understands Opus in this RTMP workflow
+

A successful publish does not guarantee that every downstream player can decode the codec. Test the complete publisher → server → player chain.

+ +

A practical interoperability test plan

+
    +
  1. Record the exact OBS or FFmpeg version and command/settings.
  2. +
  3. Test one publisher and one player with H.264/AAC as a baseline.
  4. +
  5. Change only one codec at a time.
  6. +
  7. Verify initial playback and late player join.
  8. +
  9. Disconnect and reconnect the publisher.
  10. +
  11. Test multiple players and prolonged streaming.
  12. +
  13. Inspect server statistics and logs for codec detection.
  14. +
  15. Repeat over RTMPS if encrypted ingest is part of the deployment.
  16. +
+ +

Using librtmp2 in your own application

+

Developers can consume the Rust crate directly or use the generated dynamic/static library through the C-compatible FFI. The host application remains responsible for decisions such as authorization, storage, transcoding, recording, and track routing.

+
[dependencies]
+librtmp2 = "0.4"
+

While the project remains in 0.x, pin the version range appropriate for your compatibility policy and review release notes before updating.

+ +

Avoid these common assumptions

+
    +
  • Parser support does not automatically mean a feature is wired into the default session path.
  • +
  • Opaque media relay does not mean the server can transcode or inspect every codec detail.
  • +
  • Publisher support does not imply universal player support.
  • +
  • Multitrack protocol structures do not automatically create a complete multitrack control API.
  • +
  • Enhanced RTMP does not replace the need for interoperability testing across versions.
  • +
+ +
+

Check the code-accurate status

+

The repository documents what is complete, partial, parser-only, or not yet implemented.

+ +
+
+ + +
+
+
+ + diff --git a/guides/index.php b/guides/index.php new file mode 100644 index 0000000..ed040ff --- /dev/null +++ b/guides/index.php @@ -0,0 +1,60 @@ + + +
+
+ Practical guides +

Build and operate RTMP infrastructure

+

Task-focused articles for operators, application developers, and contributors. Each guide states the current alpha limitations instead of hiding them behind generic feature claims.

+
+ +
+
+
+ + + + + + +
+ Comparison · Migration +

OpenRTMP vs nginx-rtmp

+

Compare architecture, deployment, statistics compatibility, missing features, and the scenarios where each project is the more appropriate choice.

+ Read the comparison → +
+
+ +
+

Prefer a direct setup path?

+

The quickstart takes you from an empty Docker host to an OBS-ready stream in a few steps.

+ +
+
+
+
+ + diff --git a/guides/openrtmp-vs-nginx-rtmp/index.php b/guides/openrtmp-vs-nginx-rtmp/index.php new file mode 100644 index 0000000..5c29839 --- /dev/null +++ b/guides/openrtmp-vs-nginx-rtmp/index.php @@ -0,0 +1,112 @@ + 'https://schema.org', + '@type' => 'TechArticle', + 'headline' => 'OpenRTMP vs nginx-rtmp', + 'description' => $pageDescription, + 'author' => ['@type' => 'Organization', 'name' => 'OpenRTMP'], + 'mainEntityOfPage' => 'https://openrtmp.org/guides/openrtmp-vs-nginx-rtmp/' +]; +include __DIR__ . '/../../includes/header.php'; +?> + +
+
+ Comparison · Migration · Architecture +

OpenRTMP vs nginx-rtmp

+

These projects overlap at RTMP ingest and playback, but they are built around different goals. The right choice depends more on the application layer you need than on the port number they expose.

+
+ +
+
+
+
Summary: choose nginx-rtmp for established nginx workflows and built-in module features. Choose OpenRTMP for a Rust-first protocol stack, a small API-driven server, separate stream keys, JSON statistics, and an embeddable library — while accepting its alpha status and narrower feature set.
+ +

High-level comparison

+ + + + + + + + + + + + +
AreaOpenRTMPnginx-rtmp
Primary architectureRust protocol library plus separate server and panelThird-party nginx module configured through nginx directives
Project maturityActive alphaLong-established ecosystem
AdministrationREST API, SQLite, and optional web panelConfiguration files, callbacks, and surrounding nginx tooling
Stream credentialsSeparate publish, play, and stats keys per streamUsually implemented through callbacks or custom nginx configuration
StatisticsJSON plus nginx-compatible XMLClassic XML statistics endpoint with XSL presentation
Embeddable libraryRust crate and C-compatible FFINo equivalent standalone protocol crate
HLS, recording, exec, pushNot built into the current serverCommon nginx-rtmp module features
Modern RTMP workExplicit focus on RTMPS and Enhanced RTMP building blocksPrimarily traditional RTMP module workflows
+ +

Choose OpenRTMP when

+
    +
  • You are building a Rust application and want reusable RTMP protocol code.
  • +
  • You want a small self-hosted server with an explicit REST API and SQLite-backed stream registry.
  • +
  • You want separate publish, playback, and monitoring credentials.
  • +
  • You need JSON statistics but also want compatibility with tools that expect nginx-style XML.
  • +
  • You want to contribute to Enhanced RTMP, RTMPS, interoperability, or parser safety work.
  • +
  • You accept alpha software and can test the exact publishing/playback workflow.
  • +
+ +

Choose nginx-rtmp when

+
    +
  • You already operate nginx and understand its configuration model.
  • +
  • You depend on built-in HLS generation, recording, exec hooks, or push relay.
  • +
  • You need a mature deployment pattern with extensive existing examples.
  • +
  • Your monitoring and automation are already built directly around nginx-rtmp behavior.
  • +
  • You do not need an embeddable Rust/C protocol library.
  • +
+ +

Migration considerations

+

Statistics integrations

+

OpenRTMP exposes /stats-nginx?key=<stats_key> for tools that understand nginx-rtmp XML. The endpoint intentionally protects each stream with its statistics key, so URL construction differs from a public server-wide stats page.

+

Authentication

+

nginx-rtmp deployments often use on_publish and on_play HTTP callbacks. OpenRTMP keeps stream records and keys in SQLite and validates them inside the server application layer.

+

Configuration model

+

Do not attempt to translate every nginx directive one-to-one. OpenRTMP is not an nginx module and deliberately lacks several nginx-rtmp application features.

+

Media features

+

If your nginx configuration records, transcodes, packages HLS, or pushes to other destinations, retain those services or add separate components before migrating ingest.

+ +

They can coexist

+

A migration does not have to be all-or-nothing. You can test OpenRTMP on a separate port or host, compare OBS/FFmpeg behavior, and keep nginx-rtmp for HLS or relay tasks while evaluating the OpenRTMP API and key model.

+

A useful staged test is:

+
    +
  1. Reproduce one H.264/AAC publisher and player workflow.
  2. +
  3. Compare startup, reconnect, late join, and monitoring behavior.
  4. +
  5. Validate the nginx-compatible XML with existing automation.
  6. +
  7. Inventory every nginx directive currently used.
  8. +
  9. Keep unsupported functions in nginx or replace them with explicit services.
  10. +
+ +

OpenRTMP limitations to account for

+

The current server is not a drop-in nginx-rtmp replacement. It does not provide built-in HLS, exec, push relay, recording, or full nginx directive parity. The protocol and public APIs are still evolving before 1.0.

+

Those limitations are acceptable when the desired system is a focused RTMP endpoint or an embeddable protocol stack. They are blockers when the existing nginx configuration is acting as a full media workflow engine.

+ +
+

Evaluate OpenRTMP without replacing nginx

+

Run the Docker stack on an alternate host or port and test one workflow end to end.

+ +
+
+ + +
+
+
+ + diff --git a/guides/rtmps-server-obs/index.php b/guides/rtmps-server-obs/index.php new file mode 100644 index 0000000..2f3f8b9 --- /dev/null +++ b/guides/rtmps-server-obs/index.php @@ -0,0 +1,124 @@ + 'https://schema.org', + '@type' => 'TechArticle', + 'headline' => 'Configure an RTMPS server for OBS', + 'description' => $pageDescription, + 'author' => ['@type' => 'Organization', 'name' => 'OpenRTMP'], + 'mainEntityOfPage' => 'https://openrtmp.org/guides/rtmps-server-obs/' +]; +include __DIR__ . '/../../includes/header.php'; +?> + +
+
+ RTMPS · TLS · OBS +

Configure an RTMPS server for OBS

+

OpenRTMP can run an encrypted RTMPS listener alongside normal RTMP. Both listeners share the same stream registry, authentication keys, relay core, and connection limits.

+
+ +
+
+
+
Experimental area: RTMPS support should be tested with your exact OBS, FFmpeg, certificate, reverse-proxy, and firewall setup before production use.
+ +

How RTMPS works in OpenRTMP

+

Enabling TLS starts an additional listener. It does not replace the plaintext RTMP listener. A publisher can connect over RTMPS while a player connects over RTMP, or the other way around, because both listeners use the same server instance.

+

Typical ports are:

+ + + + + + +
ProtocolDefault example
RTMP1935/tcp
RTMPS1936/tcp
+

RTMPS is RTMP transported directly over TLS. It is not the same as placing an HTTPS reverse proxy in front of the HTTP API.

+ +

1. Prepare a certificate and private key

+

Use a certificate valid for the hostname publishers enter in OBS. The server needs readable paths to the certificate chain and matching private key.

+

For a container deployment, mount the files read-only:

+
volumes:
+  - /etc/letsencrypt/live/stream.example.com/fullchain.pem:/certs/fullchain.pem:ro
+  - /etc/letsencrypt/live/stream.example.com/privkey.pem:/certs/privkey.pem:ro
+

Do not bake private keys into a public image or repository.

+ +

2. Enable the RTMPS listener

+

Set the server configuration values:

+
TLS_ENABLED=true
+TLS_CERT_FILE=/certs/fullchain.pem
+TLS_KEY_FILE=/certs/privkey.pem
+RTMPS_BIND=0.0.0.0:1936
+

The normal RTMP_BIND remains active. Expose the additional port in Docker:

+
ports:
+  - "1935:1935"
+  - "1936:1936"
+

The server refuses to enable TLS when either certificate path is missing, which prevents a false-positive configuration where the health endpoint suggests encrypted ingest is available but the listener never started.

+ +

3. Verify the server before changing OBS

+

Check the health endpoint:

+
curl https://api.example.com/api/v1/health
+

The response should indicate that RTMPS is enabled and identify the bound port. Also verify the certificate from the client network:

+
openssl s_client -connect stream.example.com:1936 \
+  -servername stream.example.com
+

Look for a successful certificate chain and confirm that the hostname matches.

+ +

4. Connect OBS over RTMPS

+
    +
  1. Open OBS Settings → Stream.
  2. +
  3. Select a custom service.
  4. +
  5. Use rtmps://stream.example.com:1936/live as the server.
  6. +
  7. Use the OpenRTMP publish_key as the stream key.
  8. +
  9. Start streaming and watch the server logs and panel statistics.
  10. +
+

The panel only displays RTMPS URLs when the server health endpoint reports that the TLS listener is active.

+ +

Network and certificate pitfalls

+

Port 1936 is closed

+

Open the TCP port in the host firewall, cloud security group, Docker mapping, and any upstream network firewall.

+

The certificate is valid in a browser but OBS rejects it

+

Confirm that the full chain is supplied, not only the leaf certificate. Test from the same network and hostname OBS uses.

+

A TCP proxy terminates TLS first

+

Decide whether OpenRTMP or the proxy owns TLS. If the proxy terminates TLS, it must forward plaintext RTMP to the RTMP listener. If OpenRTMP owns TLS, use TCP passthrough rather than HTTP proxying.

+

The panel still shows only RTMP URLs

+

Verify that the panel can reach the current health endpoint, that RTMPS reports enabled, and that LRTMP2_RTMPS_PORT matches the public port.

+ +

Security recommendations

+
    +
  • Use a publicly trusted certificate for internet-facing publishers.
  • +
  • Automate renewal and restart or reload the server after certificate replacement.
  • +
  • Keep the private key readable only by the service account or container.
  • +
  • Retain publish-key authentication even when transport is encrypted.
  • +
  • Monitor failed handshakes and repeated authentication failures.
  • +
  • Test both RTMP and RTMPS after every certificate or network change.
  • +
+ +
+

Start from the Docker deployment

+

Deploy the basic stack first, then add certificates and the second listener.

+ +
+
+ + +
+
+
+ + diff --git a/guides/self-hosted-rtmp-server-docker/index.php b/guides/self-hosted-rtmp-server-docker/index.php new file mode 100644 index 0000000..7005768 --- /dev/null +++ b/guides/self-hosted-rtmp-server-docker/index.php @@ -0,0 +1,136 @@ + 'https://schema.org', + '@type' => 'TechArticle', + 'headline' => 'Self-host an RTMP server with Docker and OBS', + 'description' => $pageDescription, + 'author' => ['@type' => 'Organization', 'name' => 'OpenRTMP'], + 'publisher' => ['@type' => 'Organization', 'name' => 'OpenRTMP'], + 'mainEntityOfPage' => 'https://openrtmp.org/guides/self-hosted-rtmp-server-docker/' +]; +include __DIR__ . '/../../includes/header.php'; +?> + +
+
+ Docker · OBS · Self-hosted +

Self-host an RTMP server with Docker

+

Use OpenRTMP when you want a focused private RTMP/RTMPS endpoint with stream-key authentication, REST API, live statistics, and a browser control panel.

+
+ +
+
+
+
Status: OpenRTMP is active alpha software. Test failure recovery, reconnect behavior, codecs, and every client you rely on before critical production use.
+ +

What the Docker stack runs

+

The quickstart stack separates protocol handling, application policy, and user interface:

+
    +
  • librtmp2-server: RTMP listener, optional RTMPS listener, SQLite registry, key validation, REST API, and statistics.
  • +
  • librtmp2-server-panel: browser UI for creating streams, copying URLs and keys, and viewing live data.
  • +
  • Redis: shared rate-limit state for the multi-worker panel deployment.
  • +
+

The protocol library is embedded in the server. You do not have to install Rust when using the published container images.

+ +

Deploy locally

+

Follow the five-minute quickstart for the complete copy-and-paste commands. The short path is:

+
git clone https://github.com/OpenRTMP/librtmp2-server-panel.git
+cd librtmp2-server-panel
+# Create .env with LRTMP2_API_TOKEN, PASSWORD, and SECRET_KEY.
+docker compose -f compose.quickstart.yml up -d
+

The stack exposes:

+ + + + + + + +
PortPurposePublic exposure
1935/tcpRTMP publishers and playersExpose when remote RTMP clients need it
8000/tcpWeb control panelPrefer HTTPS through a reverse proxy
8080/tcpREST API and statisticsRestrict; proxy only required routes
+ +

Create a stream and understand its keys

+

Each stream receives separate credentials:

+ + + + + + + +
KeyUse
publish_keyAllows OBS, FFmpeg, or another publisher to send the stream
play_keyAllows a player to receive the stream
stats_keyAllows access to that stream's monitoring endpoint
+

Keeping these concerns separate means a monitoring integration does not need permission to publish or play media.

+ +

Publish from OBS

+
    +
  1. Create a stream in the panel.
  2. +
  3. Open OBS Settings → Stream.
  4. +
  5. Select a custom service.
  6. +
  7. Use the panel's RTMP server URL, normally rtmp://host:1935/live.
  8. +
  9. Use the generated publish_key as the stream key.
  10. +
+

When the publisher connects, the panel should show bitrate, codec, resolution, frame rate, RTT, uptime, and connected player data.

+ +

Monitoring and NOALBS-style statistics

+

OpenRTMP offers two statistics formats:

+
    +
  • /stats?key=<stats_key> returns JSON.
  • +
  • /stats-nginx?key=<stats_key> returns nginx-rtmp-compatible XML for existing tooling.
  • +
+

The XML compatibility endpoint is useful when an integration expects the classic nginx-rtmp statistics shape. New integrations should generally prefer JSON.

+ +

Internet-facing deployment checklist

+
    +
  • Use strong random values for the API token, panel password, and Flask session secret.
  • +
  • Set LRTMP2_DOMAIN to the public hostname clients actually use.
  • +
  • Put the panel behind HTTPS and enable secure cookies.
  • +
  • Do not expose the administrative REST API broadly.
  • +
  • Enable RTMPS when publishers need encrypted transport.
  • +
  • Back up the persistent SQLite volume.
  • +
  • Pin tested container versions instead of using moving tags.
  • +
  • Apply connection, request-body, and rate limits appropriate for the host.
  • +
  • Test container restart, host reboot, publisher reconnect, and late player join behavior.
  • +
+ +

When OpenRTMP is the right fit

+

OpenRTMP is a strong candidate for private ingest, focused RTMP experiments, custom control planes, stream-key management, or integrations that want Rust and a small understandable architecture.

+

Choose a broader media platform when you need built-in HLS delivery, recording, transcoding, push relay, a public viewer website, or many non-RTMP protocols without writing additional services.

+ +

Troubleshooting

+

Panel loads, but stream creation fails

+

Check that the panel and server share the same API token and that the server container is healthy. A reused SQLite volume may still contain an older token.

+

OBS times out

+

Verify the public firewall and Docker port mapping for 1935/tcp. Confirm the application path and use the publish key, not the play or stats key.

+

Statistics links are unreachable from another computer

+

Replace localhost values in LRTMP2_DOMAIN and LRTMP2_STATS_URL with browser-reachable addresses.

+ +
+

Deploy the stack

+

Use the dedicated quickstart, then return here for the production checklist.

+ +
+
+ + +
+
+
+ + diff --git a/includes/footer.php b/includes/footer.php index 5fca92f..8f36f8f 100644 --- a/includes/footer.php +++ b/includes/footer.php @@ -6,41 +6,40 @@ OpenRTMP logo OpenRTMP.org
-

- A pure RTMP/E-RTMP protocol library for Rust, plus - librtmp2-server (reference media server) and - librtmp2-server-panel (web management UI). - The library handles only the wire protocol — your app - (or the server) owns auth, storage, and HTTP. +

+ Modern RTMP infrastructure for developers and stream operators: + a Rust RTMP/E-RTMP library, a self-hosted RTMP/RTMPS server, + REST API, live statistics, and a web control panel. +

+

+ Active alpha. Pin versions and validate your complete workflow before critical production use.

-

Project

+

Get started

-

Repositories

+

Projects

Community

@@ -51,9 +50,9 @@ Legal Notice
- Rust - RTMP / E-RTMP v1 & v2 - AMF0 / AMF3 + Rust + C FFI + RTMP / RTMPS + E-RTMP v1 / v2
diff --git a/includes/header.php b/includes/header.php index 4a70c7b..783217b 100644 --- a/includes/header.php +++ b/includes/header.php @@ -1,15 +1,36 @@ -<?php echo $pageTitle ?? 'OpenRTMP — A modern, pure RTMP / E-RTMP protocol library'; ?> - +<?php echo htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8'); ?> + + + + + + + + + + + + + + + @@ -19,18 +40,17 @@ OpenRTMP logo OpenRTMP.org -