Real-Time Messaging Protocol

Real-Time Messaging Protocol (RTMP) is Adobe’s persistent-TCP application-layer protocol, originally introduced in 2002 by Macromedia for delivering audio, video, and arbitrary data to the Flash Player at near-real-time latency. Adobe acquired Macromedia in 2005, and partially published the specification in 2012 (Adobe’s RTMP 1.0 spec). Despite being designed for Flash playback, RTMP outlived its parent platform: when Flash Player was deprecated in 2017 and removed in 2020, RTMP’s role on the playback side died, but RTMP’s role on the ingest side — the upstream from a streamer’s encoder into a live-streaming service’s origin — survived and remained dominant through 2025. The reason is purely ecosystem inertia: every encoder (OBS Studio, Wirecast, vMix, FFmpeg, the camera firmware in countless IP cameras and broadcast appliances) speaks RTMP push out of the box, and switching them is a coordination problem with no actor large enough to drive it. The result is the canonical live-streaming pipeline used by Twitch Live Streaming System Design, YouTube Live, Facebook Live, Periscope (until shutdown 2021), and dozens of smaller services: encoder → RTMP ingest → server-side transcode to ABR ladder → HTTP-based ABR egress (Adaptive Bitrate Streaming). RTMP comes in for the upload; HLS or DASH goes out to viewers.

1. Why RTMP Survived Flash’s Death

The single most important fact about RTMP in 2026 is the asymmetry between its two historical use cases. The protocol was designed to do both sides of the streaming media path: the encoder pushed media into the server via RTMP, and the Flash Player pulled media out of the server via RTMP back to viewers. When Flash died, the pull side died with it — there are no Flash Players in the wild anymore (browsers refuse to load the plugin since 2020). The pull side is now exclusively HTTP-based ABR (HTTP Live Streaming or Dynamic Adaptive Streaming over HTTP) delivered through commodity Content Delivery Networks (Content Delivery Network System Design).

But the push side — the encoder uploading to ingest — never had any reason to switch. Protocols compete based on what changes the dominant deployed actors are willing to make. On the pull side, browsers (the dominant deployed actor) eliminated Flash and forced everyone to switch egress protocols overnight. On the push side, the dominant deployed actor is the encoder ecosystem (OBS Studio, Wirecast, FFmpeg, hardware encoders, IP camera firmware), and no comparable forcing function ever appeared. Each individual encoder vendor has zero incentive to drop RTMP support — they’d lose customers — and each ingest service has zero incentive to drop RTMP support — they’d lose streamers. The Nash equilibrium is “everyone keeps RTMP”. The result is the protocol is a zombie on the egress side and load-bearing infrastructure on the ingest side, simultaneously.

This asymmetry shapes every subsequent technical decision. When designing a live-streaming service today, the question “should I use RTMP” has two answers depending on direction: for ingest, yes, because the encoder ecosystem demands it; for egress, no, because there’s nothing alive that consumes it. SRT, RIST, and WebRTC ingest exist as parallel options for niche use cases (broadcast-grade, sub-second latency), but RTMP remains the default.

2. Origins — Flash, Macromedia, and the Web Video of 2002

In 2002 Macromedia shipped Flash Communication Server MX, a media server speaking a then-proprietary protocol called RTMP, designed to deliver streaming audio and video — and a more general “remote method invocation” message channel — to Flash Player clients. The Web at the time had no native video tag (HTML5 video was a decade away); Flash was effectively the only cross-browser way to deliver any kind of streaming media. RTMP was the protocol that made Flash video work.

Macromedia was acquired by Adobe in late 2005. RTMP remained proprietary until June 2009, when Adobe released a partial specification for RTMP under the licensing terms permitting third-party servers and clients. (Reverse-engineered open-source implementations had existed since 2008 — most notably the Red5 server and the librtmp library — and Adobe’s “release” was to a large extent a formalization of what had already been figured out.) The 2012 published spec (Parmar & Thornburgh) is the authoritative reference today.

By the early 2010s, three forces converged to make RTMP the live-streaming ingest standard:

  1. Flash Player owned video playback on the Web. Anyone wanting to deliver video to browsers used Flash, and Flash spoke RTMP. That meant every live-streaming startup (Justin.tv → Twitch Live Streaming System Design, Ustream, Livestream.com) implemented an RTMP ingest path because they needed RTMP egress anyway, and accepting RTMP-pushed input was a small additional step.
  2. Encoder ecosystem standardized on RTMP push. Adobe’s Flash Media Encoder and its successors, Wirecast, XSplit, the original streaming consoles, every IP-camera firmware shipping with an “RTMP push to URL” feature — they all spoke RTMP because Flash was the universal playback layer. By 2010 the encoder vocabulary was effectively fixed.
  3. OBS Studio (2012, free and open-source) chose RTMP as its primary streaming output, copying the Flash Media Encoder feature set. OBS won the streamer-tools market in the 2010s. By 2015 OBS-style RTMP push was the universal upload mechanism. It still is.

When Adobe announced Flash Player end-of-life in 2017 (with shutdown by end of 2020), RTMP playback died along with it. But the RTMP-on-ingest pattern was so deeply embedded in the encoder ecosystem that no alternative could displace it. Twitch, YouTube Live, and Facebook Live all continued accepting RTMP ingest — and still do as of 2026. The publicized push toward SRT (Secure Reliable Transport), RIST (Reliable Internet Stream Transport), and WebRTC ingest exists but coexists with RTMP rather than replacing it.

3. Protocol Mechanics

2.1 Wire format

RTMP runs over a single persistent TCP connection, by default on port 1935. (RTMP-over-port-443 tunneling is common to escape corporate firewalls; RTMPS — RTMP over TLS — also runs on 443 typically.) The connection lifecycle:

  1. TCP handshake — standard SYN/SYN-ACK/ACK to establish the TCP connection.
  2. RTMP handshake — three packets named C0, C1, C2 from client and S0, S1, S2 from server, each 1+1536 bytes. C0/S0 carry a 1-byte version. C1/S1 carry a 4-byte timestamp, 4 zero bytes, and 1528 bytes of random data (in current versions, ~32 bytes of “digest” derived from a Flash-Player-internal Diffie-Hellman key — purely an obscurity layer, not actual security). C2/S2 echo the random bytes back. Once the handshake completes, the connection is in “chunk stream” mode.
  3. Chunk stream phase — all subsequent traffic is a sequence of RTMP chunks. Each chunk carries a piece of an RTMP message (which is a higher-level unit — a video frame, an audio frame, a control command). A single message is split into chunks of ≤ 128 bytes by default, configurable to ≤ 65 535 via a Set Chunk Size control message.

2.2 Channels and message types

RTMP multiplexes multiple logical streams over one TCP connection using chunk stream IDs (CSIDs), which are 6-bit (or extended to 22-bit) identifiers in the chunk header. Audio packets typically use one CSID, video another, control messages a third. Within each chunk stream, messages are reassembled by sequence.

The major message types:

Type IDNamePurpose
1Set Chunk SizeNegotiate chunk size
2Abort MessageDiscard partially-received message
3AcknowledgementWindow-based flow control
4User ControlStream begin/end, ping
5Window Acknowledgement SizeFlow-control window size
6Set Peer BandwidthReceiver’s bandwidth hint
8Audio DataEncoded audio frame (typically AAC)
9Video DataEncoded video frame (typically H.264)
18AMF0 DataNotify-style metadata
20AMF0 CommandRPC-style command (connect, publish, play)

The control message types (1–6) are protocol bookkeeping; types 8 and 9 carry the media; types 18 and 20 carry AMF (Action Message Format) encoded RPC commands — a Flash-era binary serialization of name/value/object data.

2.3 The session

A typical encoder-to-ingest session walks through the AMF command sequence:

client → server: connect("live")        // attach to the "live" application instance
server → client: _result(ConnectResponse)
client → server: createStream()
server → client: _result(streamId=1)
client → server: publish("stream-key-abc123", "live")  // begin pushing media
server → client: onStatus(NetStream.Publish.Start)
client → server: @setDataFrame onMetaData {width, height, framerate, ...}
client → server: VideoData(IDR keyframe 1)
client → server: AudioData(AAC frame 1)
client → server: VideoData(P-frame 2)
client → server: AudioData(AAC frame 2)
... (sustained for the duration of the stream)
client → server: deleteStream() / disconnect

The “stream key” — stream-key-abc123 in the example — is the per-streamer secret that authenticates the upload. Twitch, YouTube Live, Facebook Live all use this pattern: the streamer copies a secret from the dashboard and pastes it into OBS, which appends it to the RTMP URL (rtmp://ingest.twitch.tv/app/<stream-key>).

2.4 The codec layer

RTMP is media-agnostic in principle but in practice the codec mix is fixed:

  • Video: H.264 (codec ID 7) is universal. H.265/HEVC was retrofitted via codec ID 12 by some implementations (notably the “Enhanced RTMP” extension that YouTube and Twitch pushed in 2022–2023) but support is fragmented. AV1 remains rare on RTMP.
  • Audio: AAC (codec ID 10) is universal. MP3 (codec ID 2) is supported in legacy contexts. Opus is not supported in stock RTMP — it requires the same Enhanced RTMP extensions.

This codec lock-in is one of RTMP’s failure modes (more in §6).

3.5 Action Message Format encoding

The _result, connect, publish, and onStatus commands listed above are encoded in Action Message Format (AMF) — Adobe’s proprietary binary serialization format from the Flash era. Two versions exist: AMF0 (the original, more verbose) and AMF3 (a compaction added in Flash Player 9). Both encode JavaScript-style objects: a marker byte for type (number, boolean, string, object, array, null, undefined, ECMA-array, strict-array, date, long-string, XML, typed-object), followed by the value.

For example, an AMF0-encoded connect invocation looks like:

02 00 07 'c''o''n''n''e''c''t'   // string marker (0x02), 7-byte length, "connect"
00 3F F0 00 00 00 00 00 00         // number marker (0x00), 64-bit double = 1.0 (transaction ID)
03                                  // object marker (0x03)
   00 03 'a''p''p'                  // property name "app"
   02 00 04 'l''i''v''e'            // string "live"
   00 08 'f''l''a''s''h''V''e''r'   // property name "flashVer"
   02 00 0D ...                     // string "FMLE/3.0 (compatible..."
   00 00 09                         // object end marker (0x000009)

The flashVer field — a self-identifying client string — is curious: it survives even when Flash itself is dead. OBS Studio sends FMLE/3.0 (compatible; FMSc/1.0) to identify itself as Flash Media Live Encoder, the historical reference. This was originally signal to the server that the client was Adobe-blessed; today it’s a vestigial handshake nobody can drop because some servers refuse connections without it.

AMF3 compaction adds reference tables (repeated strings/objects emit only an integer ref to the first occurrence), variable-length integer encoding for small numbers, and tighter object headers. The savings for typical RTMP control traffic are minor; both versions coexist.

3.6 Variants

  • RTMP — plain TCP, port 1935, no encryption. The default.
  • RTMPS — RTMP over TLS, typically port 443. Encrypts ingest. Supported by Facebook Live (mandatory) and increasingly the standard.
  • RTMPT — RTMP tunneled inside HTTP (POST requests with binary RTMP bodies). Lets RTMP traverse HTTP-only proxies. Slower (HTTP framing overhead, polling latency). Rare today.
  • RTMPTE — RTMPT + encryption. Also rare.
  • RTMPE — Adobe’s “encrypted” RTMP, using a Diffie-Hellman key exchange that was reverse-engineered shortly after introduction. Not actually secure; deprecated in favor of RTMPS.

4. The “RTMP Ingest, HLS Egress” Pattern

The dominant pattern at every modern live-streaming service:

flowchart LR
    OBS[Streamer's encoder<br/>OBS Studio / Wirecast / FFmpeg] -->|RTMP push<br/>port 1935 or 443| ING[RTMP Ingest Server<br/>regional]
    ING --> TR[Live Transcoder<br/>fan out to 5–6 ABR tiers]
    TR --> ORIG[HLS/DASH Origin]
    ORIG --> CDN[CDN Edge Caches]
    CDN --> V1[Viewer 1<br/>HLS player]
    CDN --> V2[Viewer 2<br/>HLS player]
    CDN --> V3[Viewer 3]

Walk-through. A streamer runs OBS on their laptop, capturing camera + game capture + microphone, encoding to H.264 + AAC at 1080p60 6 Mbps. OBS opens a single RTMP connection to rtmp://ingest-sjc.twitch.tv/app/live_<streamerid>_<token> and starts pushing video and audio frames every 33 ms (for 30 fps; 16.7 ms for 60 fps). The ingest server accepts the publish, validates the token, and immediately forks the incoming H.264 bitstream into the transcoding pipeline. The transcoder produces 5–6 lower bitrate renditions (e.g., 720p60 3 Mbps, 720p30 1.5 Mbps, 480p 1 Mbps, 360p 600 kbps, 160p 200 kbps) plus a “source” passthrough at the streamer’s original quality. Each rendition is segmented into 2-second HLS chunks (Adaptive Bitrate Streaming) and pushed to the origin. The HLS m3u8 manifest is updated with each new segment. CDN edges fetch on cache miss; viewers fetch from the nearest edge. The aggregate latency from the streamer’s webcam to a viewer’s screen is 5–15 seconds for normal HLS, 2–4 seconds for Low-Latency HLS — most of that latency is in the segment-buffering on the ABR side, not in RTMP ingest itself.

Why RTMP rather than direct HLS upload. A streamer could in principle produce HLS segments directly on their machine and PUT them to the service. Reasons this isn’t done:

  1. Encoder ecosystem. OBS, Wirecast, hardware encoders all speak RTMP push; very few speak “produce HLS and upload”. Changing this would require rewriting the entire encoder ecosystem.
  2. Server-side transcode. The streamer’s hardware can produce one rendition; the service needs five. RTMP ingest gives the service a single canonical input it can fan out from server-side, which has always-available CPU/GPU resources. Doing all five renditions client-side would tank the streamer’s machine.
  3. Real-time push semantics. RTMP delivers each frame as soon as it’s encoded. HLS upload would have to wait for each segment to complete (2 s minimum), adding latency on the upload side.
  4. TCP reliability. The streamer’s connection might be flaky; RTMP over TCP retransmits lost packets. The receiver gets a clean, complete bitstream. (This is also a weakness — see §6.)

4.1 Why server-side transcode is non-negotiable

Why does the service transcode rather than let the streamer’s client produce all the renditions? The answer reveals a deep asymmetry in resource availability between consumer hardware and datacenter compute. A streamer running OBS on a gaming laptop has roughly one CPU and one GPU to spare, after the game itself has consumed most of the available cycles for rendering. Encoding video is enormously expensive — even with hardware accelerators (Intel Quick Sync, NVIDIA NVENC, AMD VCE), encoding a single 1080p60 H.264 stream consumes 5–15% of a modern GPU’s encoder block. Encoding six renditions simultaneously would either monopolize the encoder or force software fallback (10× slower), tanking the streamer’s frame rate. So clients produce one stream; the service produces the rest.

A second reason is the codec/bitrate matrix. The streamer doesn’t know in advance which devices will watch — phones need 360p AAC, smart TVs need 4K HEVC, browser viewers want VP9 if available. Doing this matrix on the streamer’s side requires the streamer’s encoder to know about the service’s full target ladder. RTMP’s “push the source, the server figures it out” model decouples them: the streamer pushes one canonical input, the server’s transcode farm fans out as needed.

A third reason is post-stream processing. Recordings of the live stream become Video On Demand (VOD) assets that the service may want to re-encode at lower bitrates over time, generate thumbnails for, run automatic captioning on, or strip audio from for podcast versions. All of this requires the source to live on service-controlled infrastructure. RTMP delivers the source; the service archives it.

4.2 The transcode pipeline

A typical live transcode pipeline on the ingest server:

RTMP socket → demux → H.264 NALU stream + AAC frames
                         ↓
          ┌──────────────┼──────────────┐
          ↓              ↓              ↓
   passthrough    decode → encode    decode → encode
   (source HLS)   1080p → 720p     1080p → 360p (etc.)
          ↓              ↓              ↓
   HLS segmenter  HLS segmenter   HLS segmenter
          ↓              ↓              ↓
        ── pushed to HLS origin / object storage ──

The “passthrough” rendition reuses the source bitstream verbatim (no re-encode), packaged into HLS segments — minimum CPU. The other renditions decode the source to YUV pixels, then re-encode at the target resolution/bitrate.

Twitch’s transcoder blog post (2017) describes their custom FFmpeg-based pipeline, which they later replaced with proprietary code optimized for hardware encoders. They describe a single transcoder server handling 50–100 simultaneous live streams across all renditions, depending on the source quality.

4.3 Stream-key authentication

The standard authentication scheme on RTMP ingest is the stream key: a per-streamer secret embedded as the publish target in the RTMP URL. Twitch’s URL format rtmp://live-sjc.twitch.tv/app/live_<userid>_<token> puts the stream key (a Base64-encoded random string ~40 characters) directly in the publish path. The ingest server validates it by querying the user database and rejecting publishes whose token is invalid, revoked, or already in use.

This pattern has well-known weaknesses. Stream keys appear in URLs, which appear in nginx access logs, browser history (if the streamer accidentally pastes one into a browser), screen-recordings the streamer makes, and OBS profile files. Live-streaming community forums regularly include posts where a streamer’s key has leaked and a third party hijacked the stream. Twitch’s mitigation is per-stream-session reset on demand and one-active-stream-per-key; YouTube uses similar measures. None of these eliminate the fundamental problem that the secret travels in the URL path.

A more robust pattern would be challenge-response authentication during the RTMP connect phase — the client signs a server-issued nonce with a per-account API key. Adobe published an RTMPAuth extension along these lines, and some servers (notably Wowza) implement it. Adoption is sparse because OBS, FFmpeg, and most encoders don’t speak it; the stream key in the URL is the universal lowest common denominator.

4.4 The latency penalty of the pattern

End-to-end latency from streamer to viewer in the standard RTMP-ingest + HLS-egress pipeline is ~5–15 seconds. The breakdown:

StageLatency contribution
Encoder buffering on streamer (B-frame lookahead)100–500 ms
RTMP TCP transit + server queue50–200 ms
Transcoder pipeline (decode + encode)300–800 ms
HLS segment buffering (must accumulate full segment before publishing)2–6 s
CDN upload and propagation200–500 ms
Player buffering on viewer side (~2 segments)4–12 s
Decoder + render50–100 ms

The HLS segment buffering is the dominant cost. Low-Latency HLS (covered in Adaptive Bitrate Streaming) addresses this by exposing partial segments before completion, dropping the floor to ~2 s — but the RTMP ingest portion of the latency budget remains ~500 ms, set by the encoder lookahead and TCP transit. Sub-second latency requires replacing RTMP ingest entirely with WebRTC (Web Real-Time Communication) or SRT.

5. Real-World Deployments

Twitch. RTMP ingest is the canonical path; the Twitch ingest documentation lists a global mesh of RTMP servers (live-sjc.twitch.tv, live-ams.twitch.tv, live-tyo.twitch.tv, etc.) with automatic geo-routing. The transcoder fans out to HLS variants, distributed via Twitch’s own and partner CDNs. Twitch experimented with WebRTC ingest (“Twitch Studio Beta” using WebRTC for browser-based streaming) but RTMP remained the dominant inbound protocol. Twitch’s transcoder output is HLS with 2 s segments and supports Low-Latency HLS.

YouTube Live. RTMP ingest at rtmp://a.rtmp.youtube.com/live2/<stream-key>. Backup ingest at b.rtmp.youtube.com. RTMPS supported. HLS and DASH egress. YouTube’s “Enhanced RTMP” effort (2022) pushed HEVC and Opus support on top of vanilla RTMP; adoption was limited.

Facebook Live. RTMPS is now mandatory; plain RTMP was deprecated in 2019. Ingest at rtmps://live-api-s.facebook.com:443/rtmp/<stream-key>.

Periscope (Twitter, 2015–2021). RTMP ingest. Proprietary HLS-like egress with sub-2-second latency via aggressive segment design — the inspiration for many modern Low-Latency HLS efforts.

LinkedIn Live, TikTok Live, Twitter/X Spaces video — all RTMP-on-ingest.

Open-source RTMP servers. nginx-rtmp-module is the standard self-hosted option (a single nginx worker can handle thousands of concurrent RTMP streams). SRS (Simple Realtime Server) is the modern Chinese open-source alternative, widely deployed. Node-Media-Server and Ant Media Server fill out the ecosystem.

5.1 Open-Source RTMP Servers

The self-hosted RTMP-ingest ecosystem is dominated by a handful of open-source servers, each making different design choices:

nginx-rtmp-module — the original. An nginx C module that turns nginx into an RTMP server. Configuration via standard nginx directives. A single nginx worker handles thousands of concurrent RTMP streams; horizontal scaling via more workers (with relay directives to forward streams between instances). The module is no longer actively maintained as of 2020 (the original author moved on), but it still works and remains widely deployed in self-hosted setups. The directive syntax for a typical setup:

rtmp {
    server {
        listen 1935;
        application live {
            live on;
            record off;
            # transcode to HLS via exec
            exec ffmpeg -i rtmp://localhost:1935/live/$name
                -c:v libx264 -preset veryfast -b:v 800k -f flv rtmp://localhost:1935/hls_360p/$name;
        }
        application hls_360p {
            live on;
            hls on;
            hls_path /var/www/hls/360p;
            hls_fragment 6s;
        }
    }
}

SRS (Simple Realtime Server) — the modern Chinese open-source competitor, maintained actively by a team led by Winlin since ~2013. Written in C++. Supports RTMP, HLS, HTTP-FLV, WebRTC, GB28181 (the Chinese surveillance video standard), and SRT. A single SRS instance can handle 7 000+ concurrent RTMP connections per the project’s published benchmarks. Used in production by major Chinese streaming services (Bilibili, Douyu).

Node-Media-Server — a Node.js implementation. More accessible to JavaScript developers; smaller deployment footprint; less performant than nginx-rtmp or SRS for high concurrency. Useful for prototyping and small-scale deployments.

Ant Media Server — commercial product with a free Community Edition. WebRTC-first but supports RTMP ingest. Built on Java/Tomcat. Used by some enterprise broadcasters.

MediaMTX (formerly rtsp-simple-server) — Go implementation. Handles RTMP, RTSP, HLS, WebRTC, SRT. Single static binary, easy deployment. Increasingly popular for edge deployments.

The choice between them is mostly operational preference. nginx-rtmp is the long-standing default; SRS for high-throughput; MediaMTX for protocol breadth and Go-native deployments.

5.2 Production Service Deployments

Twitch. RTMP ingest is the canonical path; the Twitch ingest documentation lists a global mesh of RTMP servers (live-sjc.twitch.tv, live-ams.twitch.tv, live-tyo.twitch.tv, etc.) with automatic geo-routing. The transcoder fans out to HLS variants, distributed via Twitch’s own and partner CDNs. Twitch experimented with WebRTC ingest (“Twitch Studio Beta” using WebRTC for browser-based streaming) but RTMP remained the dominant inbound protocol. Twitch’s transcoder output is HLS with 2 s segments and supports Low-Latency HLS.

YouTube Live. RTMP ingest at rtmp://a.rtmp.youtube.com/live2/<stream-key>. Backup ingest at b.rtmp.youtube.com. RTMPS supported. HLS and DASH egress. YouTube’s “Enhanced RTMP” effort (2022) pushed HEVC and Opus support on top of vanilla RTMP; adoption was limited.

Facebook Live. RTMPS is now mandatory; plain RTMP was deprecated in 2019. Ingest at rtmps://live-api-s.facebook.com:443/rtmp/<stream-key>.

Periscope (Twitter, 2015–2021). RTMP ingest. Proprietary HLS-like egress with sub-2-second latency via aggressive segment design — the inspiration for many modern Low-Latency HLS efforts.

LinkedIn Live, TikTok Live, Twitter/X video — all RTMP-on-ingest.

6. Modern Alternatives

The successor candidates to RTMP for ingest:

SRT (Secure Reliable Transport). Open-sourced by Haivision in 2017. Runs over UDP with selective retransmission, latency-bounded congestion control, and AES encryption. Fixes RTMP’s TCP-induced head-of-line blocking under packet loss. Widely adopted by broadcast professionals (it works great over satellite or transcontinental links where TCP performs poorly) but underadopted in streamer-grade encoders. OBS supports SRT output as of v27 (2021). Twitch added SRT ingest in some markets. SRT is technically superior for adverse networks and may slowly displace RTMP for professional ingest.

RIST (Reliable Internet Stream Transport). A standards-track alternative to SRT, developed by the RIST Forum since ~2018. Similar UDP-based design. Less encoder support than SRT, but has IETF backing. Used in broadcast scenarios.

WebRTC ingest. Web Real-Time Communication over UDP with ICE/STUN/TURN (NAT Traversal) for connection setup. Sub-second latency by design. Used in Cloudflare Stream and some specialty ingest paths. The friction is encoder-side: OBS only added experimental WHIP (WebRTC-HTTP Ingestion Protocol) support in v30 (2024), and the codec/bitrate negotiation is more complex than RTMP’s “just push frames” semantics.

HLS / DASH ingest. Some services accept DASH or HLS uploads for asynchronous (file-based) ingest, but for live this requires the encoder to do segmentation and is rarely used.

The “RTMP egress is dead, RTMP ingest is alive” distinction is the most important takeaway for any system-design discussion: when designing a live-streaming service in 2026, the output path is HLS/DASH (over CDNs) or WebRTC (for sub-second). The input path will pragmatically be RTMP, with SRT as a parallel option for higher-end users.

7. Pitfalls

  1. TCP head-of-line blocking under loss. RTMP rides on a single TCP socket. A single lost packet stalls the entire stream until retransmission completes. On networks with 1–2% packet loss, this manifests as visible video freezes lasting hundreds of milliseconds. SRT/RIST/WebRTC’s UDP+selective-retransmit avoids this.
  2. Port 1935 firewalls. Corporate networks routinely block non-standard outbound ports; port 1935 is non-standard. Solutions: RTMPS on 443 (TLS-wrapped, looks like HTTPS), or RTMPT (HTTP-tunneled). The TLS-on-443 path increasingly is the default.
  3. No native authentication. RTMP itself has no authentication scheme; services overload the publish-target stream key (an unguessable URL component) as a bearer token. If logged or shared, the stream key lets anyone hijack the stream. Some services rotate stream keys per-session; many don’t.
  4. No native encryption. Vanilla RTMP is plaintext. RTMPE was Adobe’s attempt; it was broken almost immediately. RTMPS (TLS) is the right answer; it’s incrementally rolling out as the default.
  5. Codec lock-in. RTMP’s frame headers historically only encoded H.264 and AAC. HEVC, Opus, AV1 require non-standard “Enhanced RTMP” extensions and are not universally supported. A service wanting AV1 ingest must fall back to SRT or WebRTC.
  6. Single-stream per connection. RTMP multiplexes audio/video/control on one TCP connection but there’s no facility for multi-camera, multi-bitrate, or simulcast ingest from a single client. Each additional stream requires a separate TCP connection.
  7. Reconnection latency. If the TCP connection drops (e.g., client roams from WiFi to cellular), RTMP must re-handshake and re-publish from scratch. Most encoders implement automatic reconnect with a short pause; the gap is visible to viewers.
  8. High overhead at scale. A service receiving 100 000 concurrent RTMP streams maintains 100 000 long-lived TCP connections, each with kernel buffers, retransmit timers, and per-connection state. nginx-rtmp-module can handle a few thousand per box; larger deployments need horizontal sharding by stream key. UDP-based alternatives are stateless on the network layer but still need application-state per stream.
  9. Stream-key leaking via logs. Stream keys appear in URLs, which appear in nginx access logs, in monitoring dashboards, in error stack traces. Treating stream keys as secrets requires explicit log scrubbing. Several services have leaked stream keys via misconfigured CloudWatch logs.
  10. No standardization for metadata. OBS sends its own onMetaData blob; Wirecast sends another. Receivers must heuristically parse what the encoder thought to include. The Enhanced RTMP effort tried to standardize; uptake is partial.

8. Worked Example — A Streamer’s OBS Push to Twitch

Walking through one full session, hop-by-hop with timing:

T = 0 ms. Streamer clicks “Start Streaming” in OBS Studio v30. OBS has a configured target rtmp://live-sjc.twitch.tv/app/live_12345_abcdefghij (the suffix is the stream key copied from the Twitch dashboard).

T = 5 ms. OBS’s librtmp module performs DNS resolution. live-sjc.twitch.tv resolves to a regional anycast IP. Twitch’s regional ingest fleet is a load-balanced pool of nginx-rtmp boxes.

T = 25 ms. TCP three-way handshake to port 1935 of the resolved IP. RTT to Twitch San Jose datacenter from a residential connection in Oakland is ~10 ms.

T = 50 ms. RTMP handshake — C0/C1 sent, S0/S1/S2 received, C2 sent. ~25 ms of round trips with intermediate processing.

T = 80 ms. AMF command connect("app=live") sent. Server validates the application namespace exists.

T = 110 ms. AMF command releaseStream then FCPublish then createStream then publish("live_12345_abcdefghij", "live"). Server validates the stream key against the Twitch user database (stream key → user_id → entitlements). Server emits onStatus(NetStream.Publish.Start).

T = 140 ms. OBS sends @setDataFrame onMetaData with stream parameters: width=1920, height=1080, framerate=60, videocodecid=7, audiocodecid=10, videodatarate=6000, audiodatarate=160.

T = 145 ms. OBS sends the H.264 sequence header (SPS + PPS as the AVCDecoderConfigurationRecord) as a Video Data message with frame type “AVC sequence header”. The transcoder needs this to initialize its decoder.

T = 150 ms. OBS sends the AAC sequence header (AudioSpecificConfig).

T = 155 ms. OBS sends the first IDR keyframe as Video Data type 9. The keyframe is large (~200 KB at 6 Mbps × 33 ms × scaling factor for I-frames).

T = 200 ms. Twitch ingest server has the keyframe complete. The transcoder pipeline begins decoding. At the same time, the ingest server forwards the IDR + sequence headers to the “passthrough” HLS pipeline, which produces the source-quality HLS rendition.

T = 210 ms. Subsequent frames (P-frames at ~6 KB each) and AAC frames (~5 KB per 21 ms of audio at 192 kbps) flow continuously. Bitrate is ~6.16 Mbps total.

T = 2 s. First HLS segment of the source rendition is complete (2-second segments). Transcoder has decoded enough source to start emitting the lower renditions.

T = 3 s. First HLS segments of all 6 renditions are complete and pushed to the HLS origin. The Twitch player on the viewer’s side, polling the manifest, sees the new segments and begins buffering.

T = 6 s. First viewer’s screen renders frame 1. The end-to-end latency from streamer’s webcam to viewer’s screen is therefore ~6 seconds for normal HLS (~2 s for the first segment to complete + ~2 s of buffering on viewer side + ~1 s of decode/render + ~1 s of network/CDN overhead). Low-Latency HLS would shave this to ~2 seconds.

Steady state, T → infinity. OBS pushes 60 video frames/sec + ~50 AAC frames/sec to the RTMP socket, ~6 Mbps sustained. The Twitch transcoder produces 6 HLS renditions in parallel. New HLS segments appear every 2 seconds. The CDN serves them to N viewers. RTMP ingest sustains a single TCP connection with ~10 ms RTT for hours.

If the streamer’s WiFi flakes for 3 seconds (bandwidth drops to 100 kbps, then recovers): TCP buffers grow at the streamer’s end as OBS produces frames faster than they can be sent. OBS detects the back-pressure (the TCP socket’s send buffer fills) and either drops frames (if the encoder is configured to “drop late frames”) or builds a backlog that produces a several-second freeze on the ingest server. When WiFi recovers, the backlog flushes; the transcoder receives a burst and processes them as fast as it can. Viewers see a freeze, then catch-up. UDP-based ingest with framedropping (SRT, WebRTC) handles this much more gracefully.

9. See Also