<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>go-rtmp</title><link>https://alxayo.github.io/rtmp-go/</link><description>Recent content on go-rtmp</description><generator>Hugo</generator><language>en</language><atom:link href="https://alxayo.github.io/rtmp-go/index.xml" rel="self" type="application/rss+xml"/><item><title>Architecture</title><link>https://alxayo.github.io/rtmp-go/docs/developer/architecture/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/developer/architecture/</guid><description>&lt;h1 id="architecture"&gt;Architecture&lt;a class="anchor" href="#architecture"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="high-level-data-flow"&gt;High-Level Data Flow&lt;a class="anchor" href="#high-level-data-flow"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every RTMP connection follows this path:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;TCP Accept → Handshake → Control Burst → Command RPC → Media Relay/Recording&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;TCP Accept&lt;/strong&gt; — the server accepts an inbound TCP connection&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handshake&lt;/strong&gt; — client and server exchange C0/C1/C2 ↔ S0/S1/S2 packets to establish the session&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control Burst&lt;/strong&gt; — both sides exchange Window Acknowledgement Size, Set Peer Bandwidth, and Set Chunk Size&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Command RPC&lt;/strong&gt; — AMF0-encoded commands (&lt;code&gt;connect&lt;/code&gt;, &lt;code&gt;createStream&lt;/code&gt;, &lt;code&gt;publish&lt;/code&gt;/&lt;code&gt;play&lt;/code&gt;) negotiate the stream&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Media Relay/Recording&lt;/strong&gt; — audio (TypeID 8) and video (TypeID 9) messages flow through the relay to subscribers and optionally to disk as FLV&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="architecture-diagram"&gt;Architecture Diagram&lt;a class="anchor" href="#architecture-diagram"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; ┌──────────────────────────────────┐
 │ TCP Listener(s) │
 │ Plain (:1935) + TLS (:1936) │
 └──────────┬───────────────────────┘
 │ Accept()
 ▼
 ┌──────────────────────────────────┐
 │ Handshake Layer │
 │ C0/C1/C2 ↔ S0/S1/S2 exchange │
 └──────────┬───────────────────────┘
 │
 ▼
 ┌──────────────────────────────────┐
 │ Chunk Layer │
 │ Message ↔ Chunk fragmentation │
 └──────────┬───────────────────────┘
 │
 ┌──────────┴───────────────────────┐
 │ │
 ┌─────▼─────┐ ┌──────▼──────┐
 │ Commands │ │ Media │
 │ (TypeID │ │ (TypeID │
 │ 20) │ │ 8=audio │
 │ │ │ 9=video) │
 └─────┬──────┘ └──────┬──────┘
 │ │
 ┌─────▼──────┐ ┌──────▼──────┐
 │ RPC Layer │ │Media Dispatch│
 │ connect │ │ Record │
 │ createStream│ │ Broadcast │
 │ publish │ │ Relay │
 │ play │ │ │
 └──────┬─────┘ └─────────────┘
 │
 ┌──────▼──────┐
 │ Event Hooks │
 └─────────────┘&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="package-map"&gt;Package Map&lt;a class="anchor" href="#package-map"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Package&lt;/th&gt;
 &lt;th&gt;Purpose&lt;/th&gt;
 &lt;th&gt;Key Types&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/handshake&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;RTMP v3 handshake (C0/C1/C2 ↔ S0/S1/S2)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Handshake&lt;/code&gt;, &lt;code&gt;State&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/chunk&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Message ↔ chunk fragmentation/reassembly&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Reader&lt;/code&gt;, &lt;code&gt;Writer&lt;/code&gt;, &lt;code&gt;ChunkHeader&lt;/code&gt;, &lt;code&gt;Message&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/amf&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;AMF0 binary codec&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;EncodeAll()&lt;/code&gt;, &lt;code&gt;DecodeAll()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/control&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Control messages (types 1-6)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Decode()&lt;/code&gt;, &lt;code&gt;Handle()&lt;/code&gt;, &lt;code&gt;Context&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/rpc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Command parsing &amp;amp; response building&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Dispatcher&lt;/code&gt;, &lt;code&gt;ConnectCommand&lt;/code&gt;, &lt;code&gt;PublishCommand&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/conn&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Connection lifecycle&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Connection&lt;/code&gt;, &lt;code&gt;Session&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/server&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Listener, stream registry, pub/sub&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Server&lt;/code&gt;, &lt;code&gt;Registry&lt;/code&gt;, &lt;code&gt;Stream&lt;/code&gt;, &lt;code&gt;Config&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/server/auth&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Token-based authentication validators&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Validator&lt;/code&gt;, &lt;code&gt;TokenValidator&lt;/code&gt;, &lt;code&gt;FileValidator&lt;/code&gt;, &lt;code&gt;CallbackValidator&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/server/hooks&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Event notification&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;HookManager&lt;/code&gt;, &lt;code&gt;Event&lt;/code&gt;, &lt;code&gt;Hook&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/media&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Audio/video parsing, codec detection (Enhanced RTMP), FLV recording&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Recorder&lt;/code&gt;, &lt;code&gt;CodecDetector&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/relay&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Multi-destination relay&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;DestinationManager&lt;/code&gt;, &lt;code&gt;Destination&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/metrics&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Expvar counters&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ConnectionsActive&lt;/code&gt;, &lt;code&gt;BytesIngested&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/rtmp/client&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Minimal RTMP client for testing&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Client&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/errors&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Domain-specific error types&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ProtocolError&lt;/code&gt;, &lt;code&gt;ChunkError&lt;/code&gt;, &lt;code&gt;AMFError&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;internal/logger&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Structured logging&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;Init()&lt;/code&gt;, &lt;code&gt;Logger()&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="connection-lifecycle"&gt;Connection Lifecycle&lt;a class="anchor" href="#connection-lifecycle"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what happens step-by-step when OBS connects and starts streaming:&lt;/p&gt;</description></item><item><title>Changelog</title><link>https://alxayo.github.io/rtmp-go/docs/project/changelog/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/project/changelog/</guid><description>&lt;h1 id="changelog"&gt;Changelog&lt;a class="anchor" href="#changelog"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;All notable changes to go-rtmp are documented here.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="unreleased"&gt;Unreleased&lt;a class="anchor" href="#unreleased"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added"&gt;Added&lt;a class="anchor" href="#added"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SRT Encryption&lt;/strong&gt; — Full AES-CTR encryption with passphrase-based key exchange (KMREQ/KMRSP), supporting AES-128/192/256 with PBKDF2 key derivation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRT Key Rotation&lt;/strong&gt; — Hitless even/odd key rekeying via post-handshake KMREQ control packets for long-running encrypted streams&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRT Reconnection Fix&lt;/strong&gt; — Second SRT connection with same stream key no longer fails after first disconnects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="security"&gt;Security&lt;a class="anchor" href="#security"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Passphrase validation (10-79 characters per SRT specification)&lt;/li&gt;
&lt;li&gt;Plaintext packets dropped on encrypted connections&lt;/li&gt;
&lt;li&gt;Strict crypto profile validation (rejects unsupported cipher types)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v020-2026-04-12"&gt;v0.2.0 (2026-04-12)&lt;a class="anchor" href="#v020-2026-04-12"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-1"&gt;Added&lt;a class="anchor" href="#added-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SRT (Secure Reliable Transport) Ingest&lt;/strong&gt;: Accept SRT streams over UDP alongside RTMP
&lt;ul&gt;
&lt;li&gt;SRT v5 handshake with SYN cookie exchange and extension negotiation&lt;/li&gt;
&lt;li&gt;Stream ID parsing: simple, prefixed, and structured formats&lt;/li&gt;
&lt;li&gt;TSBPD jitter buffer with configurable latency&lt;/li&gt;
&lt;li&gt;ACK/NAK reliability with RTT measurement and retransmission&lt;/li&gt;
&lt;li&gt;Optional AES encryption (128/192/256-bit) with PBKDF2 key derivation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MPEG-TS Demuxer&lt;/strong&gt;: Full transport stream parser with PAT/PMT table decoding, PES reassembly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codec Converters&lt;/strong&gt;: H.264/H.265 Annex B→AVCC and AAC ADTS→raw converters for SRT-to-RTMP bridge&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRT-to-RTMP Bridge&lt;/strong&gt;: End-to-end pipeline converting SRT data packets through TS demuxing and codec conversion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codec-Aware Recording&lt;/strong&gt;: Automatic container selection — FLV for H.264, MP4 for H.265/HEVC&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FLV &lt;code&gt;onMetaData&lt;/code&gt; Script Tag&lt;/strong&gt;: FLV recordings include &lt;code&gt;onMetaData&lt;/code&gt; with video dimensions, codec IDs, audio properties, and patched duration/filesize on close
&lt;ul&gt;
&lt;li&gt;MP4 recorder streams to disk in real-time (zero memory buffering)&lt;/li&gt;
&lt;li&gt;Lazy recorder initialization for correct codec detection&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress Abstraction&lt;/strong&gt;: Protocol-agnostic publish lifecycle manager shared by RTMP and SRT&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comprehensive E2E Test Suite&lt;/strong&gt;: 25+ tests in &lt;code&gt;e2e-tests/&lt;/code&gt; covering all major features&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRT CLI Flags&lt;/strong&gt;: &lt;code&gt;-srt-listen&lt;/code&gt;, &lt;code&gt;-srt-latency&lt;/code&gt;, &lt;code&gt;-srt-passphrase&lt;/code&gt;, &lt;code&gt;-srt-pbkeylen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRT Metrics&lt;/strong&gt;: 6 new expvar counters for SRT connections, bytes, packets, retransmits, drops&lt;/li&gt;
&lt;li&gt;Comprehensive package documentation and developer guide&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="changed"&gt;Changed&lt;a class="anchor" href="#changed"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;MP4 recorder streams to disk instead of buffering in memory&lt;/li&gt;
&lt;li&gt;Allocation optimizations across media handling hot paths&lt;/li&gt;
&lt;li&gt;Lazy recorder initialization for codec-aware container selection&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fixed"&gt;Fixed&lt;a class="anchor" href="#fixed"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;H.265 HEVCDecoderConfigurationRecord corrected per ISO/IEC 14496-15&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-record-all&lt;/code&gt; explicit bool flag parsing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slog.SetDefault()&lt;/code&gt; now called for consistent log levels across subsystems&lt;/li&gt;
&lt;li&gt;Three broken E2E tests (hooks and reconnect)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="new-packages"&gt;New Packages&lt;a class="anchor" href="#new-packages"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;internal/srt/&lt;/code&gt; — Full SRT protocol implementation (packet, circular, crypto, handshake, conn)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;internal/ts/&lt;/code&gt; — MPEG-TS demuxer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;internal/codec/&lt;/code&gt; — Video/audio codec converters (H.264, H.265, AAC)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;internal/ingress/&lt;/code&gt; — Publish lifecycle abstraction&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v014-2026-04-10"&gt;v0.1.4 (2026-04-10)&lt;a class="anchor" href="#v014-2026-04-10"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-2"&gt;Added&lt;a class="anchor" href="#added-2"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Enhanced RTMP (E-RTMP v2)&lt;/strong&gt;: H.265/HEVC, AV1, VP9 codec support via FourCC signaling
&lt;ul&gt;
&lt;li&gt;Compatible with FFmpeg 6.1+, OBS 29.1+, SRS 6.0+&lt;/li&gt;
&lt;li&gt;Automatic codec detection — no configuration needed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connect&lt;/code&gt; command negotiation with &lt;code&gt;fourCcList&lt;/code&gt; echo&lt;/li&gt;
&lt;li&gt;Sequence header caching for all enhanced codecs (late-join support)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Enhanced audio signaling: Opus, FLAC, AC-3, E-AC-3 via E-RTMP FourCC&lt;/li&gt;
&lt;li&gt;27 new unit tests for enhanced video/audio parsing&lt;/li&gt;
&lt;li&gt;E2E test scripts: &lt;code&gt;scripts/test-enhanced-rtmp.sh&lt;/code&gt; and &lt;code&gt;.ps1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="changed-1"&gt;Changed&lt;a class="anchor" href="#changed-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Extracted shared &lt;code&gt;fourCC()&lt;/code&gt; helper to &lt;code&gt;media/codec.go&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Simplified video diagnostic logging in registry&lt;/li&gt;
&lt;li&gt;Fixed doc comment in &lt;code&gt;connect_response.go&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v013-2026-04-09"&gt;v0.1.3 (2026-04-09)&lt;a class="anchor" href="#v013-2026-04-09"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-3"&gt;Added&lt;a class="anchor" href="#added-3"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RTMPS (TLS) support&lt;/strong&gt; — encrypted RTMP connections via TLS termination at the transport layer
&lt;ul&gt;
&lt;li&gt;New CLI flags: &lt;code&gt;-tls-listen&lt;/code&gt;, &lt;code&gt;-tls-cert&lt;/code&gt;, &lt;code&gt;-tls-key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Dual-listener architecture: plain RTMP and RTMPS simultaneously&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rtmps://&lt;/code&gt; URL support in the Go client and relay destinations&lt;/li&gt;
&lt;li&gt;Minimum TLS 1.2 enforced; TLS startup failure is fatal (no silent fallback)&lt;/li&gt;
&lt;li&gt;4 TLS integration tests with self-signed certificate helper&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform E2E testing scripts&lt;/strong&gt; — comprehensive test suite in &lt;code&gt;scripts/&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;12 scripts (6 Bash + 6 PowerShell pairs) for Linux/macOS/Windows&lt;/li&gt;
&lt;li&gt;7 E2E test cases: RTMP, RTMPS, HLS hooks, authentication (allowed + rejected), combined TLS + auth&lt;/li&gt;
&lt;li&gt;Helper scripts: dependency checker, TLS cert generator, parameterized server launcher, HLS hook&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-platform build scripts&lt;/strong&gt; — &lt;code&gt;scripts/build.sh&lt;/code&gt; and &lt;code&gt;scripts/build.ps1&lt;/code&gt; for local compilation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hugo documentation site&lt;/strong&gt; — full docs with GitHub Pages deployment, Hugo-book theme&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="fixed-1"&gt;Fixed&lt;a class="anchor" href="#fixed-1"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Shell hook Windows compatibility (&lt;code&gt;powershell.exe&lt;/code&gt; detection instead of hardcoded &lt;code&gt;/bin/bash&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Docs workflow Hugo version bump for theme compatibility&lt;/li&gt;
&lt;li&gt;GitHub Pages deployment configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v012-2026-03-04"&gt;v0.1.2 (2026-03-04)&lt;a class="anchor" href="#v012-2026-03-04"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-4"&gt;Added&lt;a class="anchor" href="#added-4"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expvar metrics&lt;/strong&gt; — live counters exposed via HTTP &lt;code&gt;/debug/vars&lt;/code&gt; endpoint (&lt;code&gt;-metrics-addr&lt;/code&gt; flag)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disconnect handlers&lt;/strong&gt; — proper cleanup of publisher/subscriber registrations on connection close&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TCP deadline enforcement&lt;/strong&gt; — read 90s / write 30s deadlines for zombie detection, reset on each I/O operation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lifecycle hook events&lt;/strong&gt; — new events: &lt;code&gt;connection_close&lt;/code&gt;, &lt;code&gt;publish_stop&lt;/code&gt;, &lt;code&gt;play_stop&lt;/code&gt;, &lt;code&gt;subscriber_count&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="improved"&gt;Improved&lt;a class="anchor" href="#improved"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Performance optimizations&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;AMF0 decode: reduced allocations in object/array parsing&lt;/li&gt;
&lt;li&gt;Chunk writer: buffer reuse to avoid repeated allocation&lt;/li&gt;
&lt;li&gt;RPC: lazy initialization of command dispatcher&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dead code removal&lt;/strong&gt;: removed unused &lt;code&gt;bufpool&lt;/code&gt; package, &lt;code&gt;ErrForbidden&lt;/code&gt; sentinel, and unused &lt;code&gt;Session&lt;/code&gt; type&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v011-2026-03-03"&gt;v0.1.1 (2026-03-03)&lt;a class="anchor" href="#v011-2026-03-03"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-5"&gt;Added&lt;a class="anchor" href="#added-5"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token-based authentication&lt;/strong&gt; with 4 backends:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;token&lt;/code&gt; — static stream key/token pairs via CLI flags&lt;/li&gt;
&lt;li&gt;&lt;code&gt;file&lt;/code&gt; — JSON file with stream key/token mappings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;callback&lt;/code&gt; — webhook URL for external auth validation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt; — open access (default)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL query parameter parsing&lt;/strong&gt; — tokens extracted from &lt;code&gt;?token=value&lt;/code&gt; in stream URLs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;EventAuthFailed&lt;/code&gt;&lt;/strong&gt; hook event — fires when authentication fails&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="v010-2025-10-18"&gt;v0.1.0 (2025-10-18)&lt;a class="anchor" href="#v010-2025-10-18"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="added-6"&gt;Added&lt;a class="anchor" href="#added-6"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First feature-complete release of go-rtmp.&lt;/p&gt;</description></item><item><title>OBS Studio Setup</title><link>https://alxayo.github.io/rtmp-go/docs/guides/obs-setup/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/guides/obs-setup/</guid><description>&lt;h1 id="obs-studio-setup"&gt;OBS Studio Setup&lt;a class="anchor" href="#obs-studio-setup"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;This guide walks through configuring &lt;a href="https://obsproject.com/"&gt;OBS Studio&lt;/a&gt; to stream to your go-rtmp server.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;go-rtmp server running (see &lt;a href="https://alxayo.github.io/rtmp-go/docs/quick-start/"&gt;Quick Start&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;OBS Studio installed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-by-step-configuration"&gt;Step-by-Step Configuration&lt;a class="anchor" href="#step-by-step-configuration"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="1-stream-settings"&gt;1. Stream Settings&lt;a class="anchor" href="#1-stream-settings"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open OBS → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Stream&lt;/strong&gt;:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Setting&lt;/th&gt;
 &lt;th&gt;Value&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Service&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;Custom&amp;hellip;&lt;/strong&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Server&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;rtmp://localhost:1935/live&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stream Key&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;mystream&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote class='book-hint '&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Replace &lt;code&gt;localhost&lt;/code&gt; with your server&amp;rsquo;s IP address if streaming from a different machine.&lt;/p&gt;
&lt;/blockquote&gt;&lt;h3 id="2-output-settings"&gt;2. Output Settings&lt;a class="anchor" href="#2-output-settings"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open OBS → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Output&lt;/strong&gt; → &lt;strong&gt;Streaming&lt;/strong&gt; tab:&lt;/p&gt;</description></item><item><title>Recording</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/recording/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/recording/</guid><description>&lt;h1 id="recording"&gt;Recording&lt;a class="anchor" href="#recording"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp records published streams to disk automatically. The server selects the container format based on the video codec:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;H.264/AVC streams&lt;/strong&gt; → &lt;strong&gt;FLV&lt;/strong&gt; (Flash Video) container&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;H.265/HEVC streams&lt;/strong&gt; → &lt;strong&gt;MP4&lt;/strong&gt; (ISO BMFF) container&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recording runs alongside live relay — subscribers see the stream in real-time while the server simultaneously writes to disk.&lt;/p&gt;
&lt;h2 id="enabling-recording"&gt;Enabling Recording&lt;a class="anchor" href="#enabling-recording"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add the &lt;code&gt;-record-all&lt;/code&gt; and &lt;code&gt;-record-dir&lt;/code&gt; flags:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./rtmp-server -record-all true -record-dir ./recordings&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The directory is created automatically if it doesn&amp;rsquo;t exist. By default, &lt;code&gt;-record-dir&lt;/code&gt; points to &lt;code&gt;recordings&lt;/code&gt; in the working directory.&lt;/p&gt;</description></item><item><title>Design Principles</title><link>https://alxayo.github.io/rtmp-go/docs/developer/design/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/developer/design/</guid><description>&lt;h1 id="design-principles"&gt;Design Principles&lt;a class="anchor" href="#design-principles"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;These principles guide every decision in the go-rtmp codebase. When in doubt, refer back to these.&lt;/p&gt;
&lt;h2 id="correctness-over-features"&gt;Correctness Over Features&lt;a class="anchor" href="#correctness-over-features"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every byte on the wire must match the RTMP specification. We will not ship a feature that compromises protocol correctness. If there&amp;rsquo;s a conflict between &amp;ldquo;works with most clients&amp;rdquo; and &amp;ldquo;matches the spec,&amp;rdquo; the spec wins. Golden binary vectors enforce this — every encoder and decoder is tested against exact byte sequences.&lt;/p&gt;</description></item><item><title>FFmpeg Commands</title><link>https://alxayo.github.io/rtmp-go/docs/guides/ffmpeg/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/guides/ffmpeg/</guid><description>&lt;h1 id="ffmpeg-commands"&gt;FFmpeg Commands&lt;a class="anchor" href="#ffmpeg-commands"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Common FFmpeg commands for publishing, subscribing, recording, and converting with go-rtmp.&lt;/p&gt;
&lt;h2 id="publishing"&gt;Publishing&lt;a class="anchor" href="#publishing"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="from-a-file"&gt;From a File&lt;a class="anchor" href="#from-a-file"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Re-stream a video file at its original frame rate:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ffmpeg -re -i video.mp4 -c copy -f flv rtmp://localhost:1935/live/test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-re&lt;/code&gt; reads the file at its native frame rate (real-time). Without this flag, FFmpeg sends frames as fast as possible.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c copy&lt;/code&gt; copies the codec without re-encoding (fast, no quality loss).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="test-pattern-no-file-needed"&gt;Test Pattern (No File Needed)&lt;a class="anchor" href="#test-pattern-no-file-needed"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Generate a synthetic video + audio stream for testing:&lt;/p&gt;</description></item><item><title>Live Relay</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/relay/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/relay/</guid><description>&lt;h1 id="live-relay"&gt;Live Relay&lt;a class="anchor" href="#live-relay"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Live relay is the core of go-rtmp. It enables real-time pub/sub streaming: one publisher sends media to a stream key, and any number of subscribers receive it simultaneously.&lt;/p&gt;
&lt;h2 id="how-pubsub-works"&gt;How Pub/Sub Works&lt;a class="anchor" href="#how-pubsub-works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The server maintains a &lt;strong&gt;stream registry&lt;/strong&gt; — a thread-safe map of active streams keyed by their full stream key (e.g., &lt;code&gt;live/test&lt;/code&gt;).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;publisher&lt;/strong&gt; connects and issues a &lt;code&gt;publish&lt;/code&gt; command for a stream key&lt;/li&gt;
&lt;li&gt;The server creates a stream entry in the registry&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscribers&lt;/strong&gt; connect and issue &lt;code&gt;play&lt;/code&gt; commands for the same stream key&lt;/li&gt;
&lt;li&gt;Each media message from the publisher is broadcast to all active subscribers&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Only one publisher is allowed per stream key. Attempting to publish to an occupied key results in an error. There is no limit on the number of subscribers.&lt;/p&gt;</description></item><item><title>Roadmap</title><link>https://alxayo.github.io/rtmp-go/docs/project/roadmap/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/project/roadmap/</guid><description>&lt;h1 id="roadmap"&gt;Roadmap&lt;a class="anchor" href="#roadmap"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="completed"&gt;Completed&lt;a class="anchor" href="#completed"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Core RTMP protocol&lt;/strong&gt; — handshake, chunks, AMF0, commands, media relay &lt;em&gt;(v0.1.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;FLV recording&lt;/strong&gt; — automatic recording with timestamped filenames &lt;em&gt;(v0.1.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Multi-destination relay&lt;/strong&gt; — forward to YouTube, Twitch, and custom servers &lt;em&gt;(v0.1.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Event hooks&lt;/strong&gt; — webhooks, shell scripts, stdio &lt;em&gt;(v0.1.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Authentication&lt;/strong&gt; — token, file, callback, and open backends &lt;em&gt;(v0.1.1)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Expvar metrics&lt;/strong&gt; — live counters via HTTP endpoint &lt;em&gt;(v0.1.2)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;TCP deadline enforcement&lt;/strong&gt; — zombie detection and cleanup &lt;em&gt;(v0.1.2)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Performance optimizations&lt;/strong&gt; — AMF0 decode, chunk writer, RPC lazy-init &lt;em&gt;(v0.1.2)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;RTMPS (TLS)&lt;/strong&gt; — encrypted RTMP connections via TLS termination &lt;em&gt;(v0.1.3)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;E2E testing scripts&lt;/strong&gt; — cross-platform Bash + PowerShell test suite for RTMP/RTMPS/HLS/auth &lt;em&gt;(v0.1.3)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Enhanced RTMP (E-RTMP v2)&lt;/strong&gt; — H.265, AV1, VP9 codec support via FourCC signaling &lt;em&gt;(v0.1.4)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;SRT ingest&lt;/strong&gt; — accept SRT streams over UDP with transparent RTMP conversion &lt;em&gt;(v0.2.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;MPEG-TS demuxer&lt;/strong&gt; — full transport stream parser for SRT-to-RTMP bridge &lt;em&gt;(v0.2.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Codec converters&lt;/strong&gt; — H.264/H.265/AAC format converters for protocol bridging &lt;em&gt;(v0.2.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Codec-aware recording&lt;/strong&gt; — automatic FLV/MP4 container selection based on codec &lt;em&gt;(v0.2.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Comprehensive E2E test suite&lt;/strong&gt; — 25+ tests covering all features with cross-platform runners &lt;em&gt;(v0.2.0)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;SRT Encryption&lt;/strong&gt; — AES-CTR encryption with passphrase, key rotation &lt;em&gt;(v0.4.0)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="in-progress"&gt;In Progress&lt;a class="anchor" href="#in-progress"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;Fuzz testing&lt;/strong&gt; — fuzz testing for AMF0 and chunk parsing to find edge cases and crashes&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="planned"&gt;Planned&lt;a class="anchor" href="#planned"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;📋 &lt;strong&gt;Configurable backpressure&lt;/strong&gt; — tunable queue sizes, drop policies, and subscriber eviction strategies&lt;/li&gt;
&lt;li&gt;📋 &lt;strong&gt;Clustering &amp;amp; HA&lt;/strong&gt; — multi-node stream distribution with failover&lt;/li&gt;
&lt;li&gt;📋 &lt;strong&gt;DVR / Time-shift&lt;/strong&gt; — seek-back into recorded streams for live rewind&lt;/li&gt;
&lt;li&gt;📋 &lt;strong&gt;Transcoding&lt;/strong&gt; — on-the-fly quality adaptation (ABR) for multi-bitrate delivery &lt;em&gt;(note: H.265/HEVC is now natively supported for passthrough via E-RTMP v2 and SRT ingest)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-contribute"&gt;How to Contribute&lt;a class="anchor" href="#how-to-contribute"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re interested in working on any planned feature, open an issue to discuss the approach before starting. See the &lt;a href="https://alxayo.github.io/rtmp-go/docs/developer/contributing/"&gt;Contributing Guide&lt;/a&gt; for workflow details.&lt;/p&gt;</description></item><item><title>Multi-Destination Relay</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/multi-relay/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/multi-relay/</guid><description>&lt;h1 id="multi-destination-relay"&gt;Multi-Destination Relay&lt;a class="anchor" href="#multi-destination-relay"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp can forward published streams to one or more external RTMP servers. This enables simulcasting, CDN distribution, and backup recording without any transcoding.&lt;/p&gt;
&lt;h2 id="enabling-relay"&gt;Enabling Relay&lt;a class="anchor" href="#enabling-relay"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the &lt;code&gt;-relay-to&lt;/code&gt; flag, which can be specified multiple times:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./rtmp-server -listen :1935 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -relay-to rtmp://cdn1.example.com/live/key &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -relay-to rtmp://cdn2.example.com/live/key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each destination must use the &lt;code&gt;rtmp://&lt;/code&gt; scheme and include a host. Destinations are validated at startup — invalid URLs cause the server to exit with an error.&lt;/p&gt;</description></item><item><title>Troubleshooting</title><link>https://alxayo.github.io/rtmp-go/docs/guides/troubleshooting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/guides/troubleshooting/</guid><description>&lt;h1 id="troubleshooting"&gt;Troubleshooting&lt;a class="anchor" href="#troubleshooting"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference&lt;a class="anchor" href="#quick-reference"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Symptom&lt;/th&gt;
 &lt;th&gt;Cause&lt;/th&gt;
 &lt;th&gt;Fix&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&amp;ldquo;connection refused&amp;rdquo;&lt;/td&gt;
 &lt;td&gt;Server not running&lt;/td&gt;
 &lt;td&gt;Start the server first&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Black screen in ffplay&lt;/td&gt;
 &lt;td&gt;Missing sequence headers&lt;/td&gt;
 &lt;td&gt;Restart publisher; wait 2–3s before starting subscriber&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&amp;ldquo;stream not found&amp;rdquo;&lt;/td&gt;
 &lt;td&gt;Wrong stream key&lt;/td&gt;
 &lt;td&gt;Ensure publisher and subscriber use the same &lt;code&gt;app/streamName&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;High CPU&lt;/td&gt;
 &lt;td&gt;Debug logging&lt;/td&gt;
 &lt;td&gt;Use &lt;code&gt;-log-level info&lt;/code&gt; instead of &lt;code&gt;debug&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Recording file empty&lt;/td&gt;
 &lt;td&gt;Publisher disconnected before keyframe&lt;/td&gt;
 &lt;td&gt;Stream for at least a few seconds&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Connection dropped after ~90s&lt;/td&gt;
 &lt;td&gt;TCP read deadline&lt;/td&gt;
 &lt;td&gt;Normal for idle connections — ensure publisher is actively streaming&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;H.264 &amp;ldquo;mmco: unref short&amp;rdquo; warning&lt;/td&gt;
 &lt;td&gt;Joined mid-GOP&lt;/td&gt;
 &lt;td&gt;Normal and expected — decoder recovers in &amp;lt;1s&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Auth failure&lt;/td&gt;
 &lt;td&gt;Token mismatch&lt;/td&gt;
 &lt;td&gt;Check stream key and token match exactly&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Hook not firing&lt;/td&gt;
 &lt;td&gt;Wrong event name&lt;/td&gt;
 &lt;td&gt;Verify event type spelling (&lt;code&gt;connection_accept&lt;/code&gt;, &lt;code&gt;publish_start&lt;/code&gt;, etc.)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="connection-issues"&gt;Connection Issues&lt;a class="anchor" href="#connection-issues"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="connection-refused"&gt;&amp;ldquo;Connection Refused&amp;rdquo;&lt;a class="anchor" href="#connection-refused"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The server is not running or not listening on the expected address.&lt;/p&gt;</description></item><item><title>Authentication</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/authentication/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/authentication/</guid><description>&lt;h1 id="authentication"&gt;Authentication&lt;a class="anchor" href="#authentication"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp supports pluggable token-based authentication to control who can publish and subscribe to streams.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;a class="anchor" href="#overview"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Mode&lt;/th&gt;
 &lt;th&gt;Flag&lt;/th&gt;
 &lt;th&gt;Best For&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-auth-mode none&lt;/code&gt; (default)&lt;/td&gt;
 &lt;td&gt;Open access, backward compatible&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;token&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-auth-mode token&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Small setups, static configuration&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-auth-mode file&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Medium deployments, live reload&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;callback&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;-auth-mode callback&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Full integration with existing auth systems&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Authentication is enforced at the &lt;strong&gt;publish/play command level&lt;/strong&gt; — not at connect or handshake. This means the RTMP connection is established first, then auth is checked when the client issues a publish or play command.&lt;/p&gt;</description></item><item><title>E2E Testing Scripts</title><link>https://alxayo.github.io/rtmp-go/docs/guides/e2e-testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/guides/e2e-testing/</guid><description>&lt;h1 id="end-to-end-testing-scripts"&gt;End-to-End Testing Scripts&lt;a class="anchor" href="#end-to-end-testing-scripts"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp includes a comprehensive suite of cross-platform E2E testing scripts in the &lt;code&gt;scripts/&lt;/code&gt; directory. These scripts validate the full streaming pipeline — from publishing through relay to playback — covering plain RTMP, RTMPS (TLS), HLS via hooks, and authentication.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;a class="anchor" href="#prerequisites"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Check that all required tools are available:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux/macOS:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./scripts/check-deps.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Windows (PowerShell):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.\scripts\check-deps.ps1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This verifies: &lt;code&gt;ffmpeg&lt;/code&gt;, &lt;code&gt;ffplay&lt;/code&gt;, &lt;code&gt;ffprobe&lt;/code&gt; in PATH, and that the &lt;code&gt;rtmp-server&lt;/code&gt; binary exists. Each tool&amp;rsquo;s version is reported.&lt;/p&gt;</description></item><item><title>Event Hooks</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/hooks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/hooks/</guid><description>&lt;h1 id="event-hooks"&gt;Event Hooks&lt;a class="anchor" href="#event-hooks"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp fires hooks on RTMP lifecycle events, allowing external systems to react to stream activity in real-time. Hooks are &lt;strong&gt;asynchronous&lt;/strong&gt; — they never block RTMP message processing.&lt;/p&gt;
&lt;h2 id="available-events"&gt;Available Events&lt;a class="anchor" href="#available-events"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Event&lt;/th&gt;
 &lt;th&gt;Trigger&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;connection_accept&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Client TCP connection accepted&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;connection_close&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Client disconnected&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;handshake_complete&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;RTMP handshake finished&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;stream_create&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Stream first created in registry&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;stream_delete&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Stream removed (no publishers or subscribers)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;publish_start&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Publisher begins streaming&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;publish_stop&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Publisher stops streaming&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;play_start&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Subscriber begins playback&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;play_stop&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Subscriber stops playback&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;codec_detected&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Audio/video codec identified&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;subscriber_count&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Subscriber count changed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;auth_failed&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Authentication attempt failed&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="event-payload"&gt;Event Payload&lt;a class="anchor" href="#event-payload"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every event is delivered as a JSON object:&lt;/p&gt;</description></item><item><title>RTMPS (TLS)</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/rtmps/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/rtmps/</guid><description>&lt;h1 id="rtmps-tls-encryption"&gt;RTMPS (TLS Encryption)&lt;a class="anchor" href="#rtmps-tls-encryption"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;RTMPS adds TLS encryption to RTMP connections, protecting stream data in transit. go-rtmp implements RTMPS via &lt;strong&gt;TLS termination at the transport layer&lt;/strong&gt; — the TLS handshake wraps the TCP connection before the RTMP protocol begins, so all protocol layers (handshake, chunks, AMF, commands, media) work identically over both plain and encrypted connections.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;a class="anchor" href="#how-it-works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Publisher (OBS/FFmpeg)
 │
 │ TLS handshake (rtmps://server:1936)
 ▼
go-rtmp server
 │ tls.NewListener() wraps net.Listener
 │ Returns tls.Conn (implements net.Conn)
 ▼
RTMP protocol (identical to plain RTMP)
 │
 ▼
Subscribers, Recording, Relay, Hooks&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The server uses Go&amp;rsquo;s &lt;code&gt;crypto/tls&lt;/code&gt; package with &lt;code&gt;tls.NewListener()&lt;/code&gt; to wrap the standard TCP listener. The resulting &lt;code&gt;tls.Conn&lt;/code&gt; implements &lt;code&gt;net.Conn&lt;/code&gt;, so the entire RTMP stack — handshake, chunk parsing, AMF encoding, command dispatch, and media relay — requires zero changes. TLS is purely a transport concern.&lt;/p&gt;</description></item><item><title>Testing Guide</title><link>https://alxayo.github.io/rtmp-go/docs/developer/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/developer/testing/</guid><description>&lt;h1 id="testing-guide"&gt;Testing Guide&lt;a class="anchor" href="#testing-guide"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="running-tests"&gt;Running Tests&lt;a class="anchor" href="#running-tests"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="all-tests"&gt;All Tests&lt;a class="anchor" href="#all-tests"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="all-tests-with-race-detector"&gt;All Tests with Race Detector&lt;a class="anchor" href="#all-tests-with-race-detector"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test -race ./...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The race detector is mandatory for CI. It catches data races that would otherwise be invisible.&lt;/p&gt;
&lt;h3 id="package-level-tests"&gt;Package-Level Tests&lt;a class="anchor" href="#package-level-tests"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run tests for a specific protocol layer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/handshake/ &lt;span style="color:#75715e"&gt;# Handshake FSM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/chunk/ &lt;span style="color:#75715e"&gt;# Chunk reader/writer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/amf/ &lt;span style="color:#75715e"&gt;# AMF0 codec&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/control/ &lt;span style="color:#75715e"&gt;# Control messages&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/rpc/ &lt;span style="color:#75715e"&gt;# Command dispatch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/conn/ &lt;span style="color:#75715e"&gt;# Connection lifecycle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/server/ &lt;span style="color:#75715e"&gt;# Server integration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/server/auth/ &lt;span style="color:#75715e"&gt;# Authentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/server/hooks/ &lt;span style="color:#75715e"&gt;# Event hooks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/media/ &lt;span style="color:#75715e"&gt;# Media handling + recording&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/relay/ &lt;span style="color:#75715e"&gt;# Multi-destination relay&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/rtmp/metrics/ &lt;span style="color:#75715e"&gt;# Expvar counters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./internal/errors/ &lt;span style="color:#75715e"&gt;# Error types&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="integration-tests"&gt;Integration Tests&lt;a class="anchor" href="#integration-tests"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go test ./tests/integration/ -count&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-count=1&lt;/code&gt; flag disables test caching, ensuring tests always run fresh.&lt;/p&gt;</description></item><item><title>Contributing</title><link>https://alxayo.github.io/rtmp-go/docs/developer/contributing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/developer/contributing/</guid><description>&lt;h1 id="contributing"&gt;Contributing&lt;a class="anchor" href="#contributing"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="workflow"&gt;Workflow&lt;a class="anchor" href="#workflow"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fork&lt;/strong&gt; the repository on GitHub&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Create a feature branch&lt;/strong&gt; from &lt;code&gt;main&lt;/code&gt;:
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -b feature/NNN-description&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;Where &lt;code&gt;NNN&lt;/code&gt; is the issue number (if applicable).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make your changes&lt;/strong&gt; — write tests first, then implementation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify&lt;/strong&gt; everything passes (see below)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Submit a Pull Request&lt;/strong&gt; back to &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="branch-naming"&gt;Branch Naming&lt;a class="anchor" href="#branch-naming"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the pattern &lt;code&gt;feature/NNN-description&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;feature/042-rtmps-support
feature/099-configurable-backpressure
fix/117-amf0-null-decode
docs/update-troubleshooting&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="commit-messages"&gt;Commit Messages&lt;a class="anchor" href="#commit-messages"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Follow &lt;a href="https://www.conventionalcommits.org/"&gt;Conventional Commits&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;feat(relay): add reconnection with exponential backoff
fix(chunk): handle extended timestamp in FMT 3 continuation
test: add golden vectors for H.265 sequence headers
docs: update CLI reference with new auth flags
refactor(amf): simplify object end detection&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Scopes match package names: &lt;code&gt;handshake&lt;/code&gt;, &lt;code&gt;chunk&lt;/code&gt;, &lt;code&gt;amf&lt;/code&gt;, &lt;code&gt;control&lt;/code&gt;, &lt;code&gt;rpc&lt;/code&gt;, &lt;code&gt;conn&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt;, &lt;code&gt;auth&lt;/code&gt;, &lt;code&gt;hooks&lt;/code&gt;, &lt;code&gt;media&lt;/code&gt;, &lt;code&gt;relay&lt;/code&gt;, &lt;code&gt;metrics&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Metrics &amp; Monitoring</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/metrics/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/metrics/</guid><description>&lt;h1 id="metrics--monitoring"&gt;Metrics &amp;amp; Monitoring&lt;a class="anchor" href="#metrics--monitoring"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp exposes live server statistics via an HTTP endpoint using Go&amp;rsquo;s built-in &lt;code&gt;expvar&lt;/code&gt; package. Metrics are thread-safe, have zero overhead when disabled, and require no external dependencies.&lt;/p&gt;
&lt;h2 id="enabling-metrics"&gt;Enabling Metrics&lt;a class="anchor" href="#enabling-metrics"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./rtmp-server -metrics-addr :8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When &lt;code&gt;-metrics-addr&lt;/code&gt; is not specified, no HTTP listener is started and there is zero performance overhead.&lt;/p&gt;
&lt;h2 id="querying-metrics"&gt;Querying Metrics&lt;a class="anchor" href="#querying-metrics"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl http://localhost:8080/debug/vars&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The response is a JSON object containing all &lt;code&gt;rtmp_*&lt;/code&gt; and &lt;code&gt;srt_*&lt;/code&gt; keys, dynamic endpoints, and Go&amp;rsquo;s built-in &lt;code&gt;memstats&lt;/code&gt; and &lt;code&gt;cmdline&lt;/code&gt; variables:&lt;/p&gt;</description></item><item><title>HLS Streaming</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/hls-streaming/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/hls-streaming/</guid><description>&lt;h1 id="hls-streaming"&gt;HLS Streaming&lt;a class="anchor" href="#hls-streaming"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp outputs RTMP natively. To deliver streams to web browsers, convert to HLS (HTTP Live Streaming) using FFmpeg as a sidecar process.&lt;/p&gt;
&lt;h2 id="architecture"&gt;Architecture&lt;a class="anchor" href="#architecture"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Publisher (OBS/FFmpeg)
 │
 ▼
go-rtmp server (RTMP on :1935)
 │
 ▼
FFmpeg (subscribes via RTMP, outputs HLS segments)
 │
 ▼
HTTP server (serves .m3u8 + .ts files)
 │
 ▼
Browser (hls.js / native HLS player)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="basic-setup"&gt;Basic Setup&lt;a class="anchor" href="#basic-setup"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="step-1-start-go-rtmp"&gt;Step 1: Start go-rtmp&lt;a class="anchor" href="#step-1-start-go-rtmp"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./rtmp-server -listen :1935&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-2-publish-a-stream"&gt;Step 2: Publish a stream&lt;a class="anchor" href="#step-2-publish-a-stream"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;From OBS, FFmpeg, or any RTMP client:&lt;/p&gt;</description></item><item><title>SRT Ingest</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/srt-ingest/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/srt-ingest/</guid><description>&lt;h1 id="srt-ingest"&gt;SRT Ingest&lt;a class="anchor" href="#srt-ingest"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp accepts &lt;a href="https://github.com/Haivision/srt"&gt;SRT (Secure Reliable Transport)&lt;/a&gt; streams over UDP alongside RTMP. SRT publishers are transparently converted to RTMP format — existing RTMP subscribers can watch SRT sources without any changes.&lt;/p&gt;
&lt;h2 id="enabling-srt"&gt;Enabling SRT&lt;a class="anchor" href="#enabling-srt"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add the &lt;code&gt;-srt-listen&lt;/code&gt; flag to start the UDP listener:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./rtmp-server -listen :1935 -srt-listen :4200 -log-level info&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The server now accepts RTMP on TCP port 1935 and SRT on UDP port 4200 simultaneously.&lt;/p&gt;
&lt;h2 id="publishing-via-srt"&gt;Publishing via SRT&lt;a class="anchor" href="#publishing-via-srt"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="ffmpeg"&gt;FFmpeg&lt;a class="anchor" href="#ffmpeg"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ffmpeg -re -i test.mp4 -c copy -f mpegts &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;srt://localhost:4200?streamid=live/test&amp;amp;pkt_size=1316&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="obs-studio"&gt;OBS Studio&lt;a class="anchor" href="#obs-studio"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Settings → Stream → Service: &lt;strong&gt;Custom&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Server: &lt;code&gt;srt://your-server:4200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Stream Key: &lt;code&gt;live/test&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="ip-cameras"&gt;IP Cameras&lt;a class="anchor" href="#ip-cameras"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most IP cameras with SRT support can publish directly. Set the SRT destination to:&lt;/p&gt;</description></item><item><title>Multi-Stream</title><link>https://alxayo.github.io/rtmp-go/docs/user-guide/multi-stream/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://alxayo.github.io/rtmp-go/docs/user-guide/multi-stream/</guid><description>&lt;h1 id="multi-stream"&gt;Multi-Stream&lt;a class="anchor" href="#multi-stream"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;go-rtmp handles multiple simultaneous streams out of the box. Each stream is identified by a unique &lt;strong&gt;stream key&lt;/strong&gt; and operates independently — with its own publisher, subscribers, recording, and authentication. Both RTMP and SRT publishers can run side by side on the same server.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;a class="anchor" href="#how-it-works"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every stream lives in a central &lt;strong&gt;stream registry&lt;/strong&gt; keyed by stream key (e.g. &lt;code&gt;live/cam1&lt;/code&gt;, &lt;code&gt;live/cam2&lt;/code&gt;). The rules are simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One publisher per key&lt;/strong&gt; — a stream key can have exactly one active publisher at a time. A second publish attempt to the same key is rejected with an error.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlimited subscribers per key&lt;/strong&gt; — any number of viewers can watch any active stream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full isolation&lt;/strong&gt; — streams do not interact. Publishing to &lt;code&gt;live/cam1&lt;/code&gt; has no effect on &lt;code&gt;live/cam2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mixed protocols&lt;/strong&gt; — RTMP and SRT publishers register in the same registry. Subscribers see no difference.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; Publisher A (RTMP) Publisher B (SRT)
 live/cam1 live/cam2
 │ │
 ▼ ▼
 ┌──────────────────────────────────────┐
 │ Stream Registry │
 │ ┌────────────┐ ┌────────────┐ │
 │ │ live/cam1 │ │ live/cam2 │ │
 │ │ pub + subs │ │ pub + subs │ │
 │ └────────────┘ └────────────┘ │
 └──────────────────────────────────────┘
 │ │ │
 ▼ ▼ ▼
 Viewer 1 Viewer 2 Viewer 3
 (cam1) (cam1) (cam2)&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="publishing-multiple-streams"&gt;Publishing Multiple Streams&lt;a class="anchor" href="#publishing-multiple-streams"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each publisher targets a different stream key. You can mix RTMP and SRT freely — just use different keys.&lt;/p&gt;</description></item></channel></rss>