What Is SRT (vs. RTMP and HLS)
SRT (Secure Reliable Transport) is a low-latency protocol for sending video. Haivision developed it and later made it open source. It runs over UDP, resends lost packets (ARQ, Automatic Repeat reQuest), and can encrypt the stream with AES. It delivers video reliably over unstable links such as the public internet, without adding as much delay as TCP would.
How it compares with RTMP and HLS:
| Protocol | Transport | Latency | Loss Resilience | Primary Use |
|---|---|---|---|---|
| SRT | UDP (with ARQ) | Low (a few hundred ms and up) | High | Contribution/relay, field → cloud |
| RTMP | TCP | Medium | Medium (retransmission adds latency) | Ingest to streaming platforms |
| HLS | HTTP (TCP) | High (several seconds to tens of seconds) | High | Large-scale delivery to viewers |
RTMP runs over TCP, so lost packets are resent and the delay tends to build up. HLS is good at reaching large audiences, but its latency is high. That makes it a poor fit for contribution, where you send video from the field to a studio or the cloud.
SRT is built for contribution and for relaying video between sites. Its connection modes help it get through firewalls and NAT, which makes it more flexible than RTMP. A common setup receives video over SRT and converts it to HLS or DASH for viewers.
Prerequisite: Verifying libsrt Support
FFmpeg must be built with --enable-libsrt to use SRT. Check whether yours supports it:
ffmpeg -protocols | grep srt
If the output includes srt, you’re ready. If nothing is printed, that build has no libsrt support.
srt
If it isn’t supported:
- Linux: your distribution’s own package may not include it. The easiest fix is often a static build that includes libsrt, such as John Van Sickle’s builds.
- macOS: Homebrew’s
ffmpegdoesn’t include libsrt, butffmpeg-fulldoes (brew install ffmpeg-full). It isn’t added to your PATH, so run it as$(brew --prefix ffmpeg-full)/bin/ffmpeg. - Windows: the gyan.dev and BtbN builds (full / gpl editions) include libsrt.
- To build it yourself, install
libsrtfirst, then run./configure --enable-libsrt.
If an SRT URL gives
Protocol not found, your build almost certainly lacks libsrt.
Minimal Send/Receive Commands
SRT usually carries MPEG-TS (-f mpegts). SRT itself doesn’t know where video frames start and end; it only moves data in small packets (up to 1316 bytes by default). So MPEG-TS, a container that stands on its own, is the standard choice.
First, start the receiver. It waits in listener mode and saves what arrives to a file.
ffmpeg -i "srt://0.0.0.0:9000?mode=listener" -c copy received.mp4
0.0.0.0 means listen on all network interfaces, and 9000 is a UDP port of your choice. The receiver must be listening before the sender starts.
Then send video from the sender (the caller):
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316"
Replace receiver-host with the receiver’s hostname or IP address. -c copy sends the streams without re-encoding, and -f mpegts wraps them in MPEG-TS.
-rereads the input at its normal playback speed (its frame rate). You need it to send a file in real time as if it were live. Without it, FFmpeg reads the file as fast as it can, and the result is not a live stream.pkt_size=1316is the amount of data in each UDP packet (see below).
The simplest setup is mode=listener on the receiver and caller (the default when mode is left out) on the sender. You can also swap them, with the receiver as caller and the sender as listener. Any arrangement works as long as one side listens and the other connects.
Connection Modes (caller / listener / rendezvous)
The connection mode decides which side starts the connection. Set it with mode= in the URL.
| Mode | Behavior | Typical Use |
|---|---|---|
caller |
Connects out to the peer (the initiating side) | A sending PC transmits to a receiving server with a fixed IP |
listener |
Waits for incoming connections (the receiving side) | A receiving server in the cloud waits for connections |
rendezvous |
Both sides initiate the connection simultaneously | When both sides are behind NAT/firewalls (not usable for this in FFmpeg; see below) |
caller ↔ listener (the basic setup): one side is listener and waits, the other is caller and connects. The caller must be able to reach the listener’s public IP address and port.
# Receiver (listener)
ffmpeg -i "srt://0.0.0.0:9000?mode=listener" -c copy received.mp4
# Sender (caller, the default when mode is omitted)
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316"
rendezvous: both sides start the connection at the same time. But FFmpeg’s mode=rendezvous binds its own socket to the address and port in the URL, then connects to that same address. If you put the other side’s address in the URL, it fails before connecting (on Windows: Error number -10049 occurred), and there is no option to set the local address separately. When both ends are behind NAT, use a relay server as described below.
In FFmpeg, leaving out mode always means caller, whatever the address. 0.0.0.0 alone does not make it listen, so always add mode=listener on the side that waits.
Key Parameters (pkt_size / latency / mode)
SRT settings go at the end of the URL, as ?key=value&key2=value2.
pkt_size
?pkt_size=1316
pkt_size=1316 is the amount of data (in bytes) in each packet. MPEG-TS packets are 188 bytes, and seven of them make 1316 (188 × 7 = 1316). Even with the IP/UDP/SRT headers added, 1316 bytes fits in a standard Ethernet MTU (1500 bytes), so packets are not split on the way (no IP fragmentation). That is why it is the usual recommendation for SRT and UDP. For MPEG-TS over SRT, pkt_size=1316 is usually all you need.
latency
?latency=200000
latency= sets the SRT receive buffer: how long the receiver waits for lost packets to be resent. In FFmpeg it is in microseconds, not milliseconds as in other SRT tools, so 200 ms is latency=200000. When the link drops packets, SRT resends them (ARQ) within this time and fills the gaps.
- A higher value leaves more time for resending, so the video breaks up less on lossy, unstable links, but the end-to-end delay grows.
- A lower value cuts the delay but copes less well with packet loss.
- The default is about 120 ms. A rule of thumb is 3–4× the RTT (round-trip time: how long data takes to get there and back), adjusted for the link quality.
For an unstable link, set the buffer explicitly:
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316&latency=200000"
Join several parameters with &.
mode
This sets which side connects (caller / listener / rendezvous). If the two sides don’t match, no connection is made, so always set them as a pair.
Re-Encoding for Delivery
-c copy is the lightest option because it sends the source as is. Re-encode when the input’s codec or keyframe layout (GOP) doesn’t fit what the receiver needs. For example, the receiver may expect H.264 + AAC in MPEG-TS, or you may want keyframes at a fixed interval.
ffmpeg -re -i input.mp4 -c:v libx264 -preset veryfast -b:v 4000k -maxrate 4000k -bufsize 8000k -pix_fmt yuv420p -g 60 -c:a aac -b:a 128k -f mpegts "srt://receiver-host:9000?pkt_size=1316"
What each option does:
| Option | Role |
|---|---|
-c:v libx264 |
Re-encode video to H.264 (broadly playable) |
-preset veryfast |
A low-latency, low-CPU preset suited to live transmission |
-b:v 4000k / -maxrate 4000k / -bufsize 8000k |
Targets a video bitrate of 4 Mbps. The rate can go above 4 Mbps for a moment (at a keyframe, for example) but not for long, and simple scenes use less |
-pix_fmt yuv420p |
Standardizes on a highly compatible pixel format |
-g 60 |
Keeps keyframes at most 60 frames apart (at most 2 s at 30 fps). Add -sc_threshold 0 for a fixed interval |
-c:a aac -b:a 128k |
Re-encodes audio to AAC at 128 kbps |
-maxrate and -bufsize keep the video from staying above 4 Mbps for long, but a keyframe can briefly go far above it, and the audio and the MPEG-TS and SRT overhead come on top. Leave spare bandwidth on the link. Regular keyframes from -g 60 bring the picture back sooner when the receiver reconnects. If the CPU is short on headroom, -preset ultrafast lowers the load further, but compresses less well.
Common Errors and Fixes
Protocol not found
FFmpeg was built without libsrt. Check with ffmpeg -protocols | grep srt. If nothing appears, switch to a build that includes libsrt (Homebrew’s ffmpeg-full, the gyan.dev full edition, the BtbN builds, a static build, and so on).
Can’t Connect (Fails to Establish Before Timeout)
The three usual causes:
- Port not open: SRT uses UDP. Allow incoming UDP on the port (for example 9000) in the receiver’s firewall or security group. Opening only TCP doesn’t work.
- Mode mismatch: one side must be
callerand the otherlistener. If both are callers or both are listeners, they never connect. - Start order: start the listener first, then the caller.
Video Breaks Up Due to Packet Loss
Blocky video or dropouts mean the buffer is too short for the packet loss on the link. Raise latency (500 ms is latency=500000).
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316&latency=500000"
If that doesn’t help, the bitrate may be too high for the link. Re-encode and lower -maxrate to leave spare bandwidth.
Both Ends Are Behind NAT and Cannot Connect
When both ends are behind NAT, the caller can’t reach the listener directly and the connection fails (FFmpeg 8.1 prints Connection to srt://... failed: I/O error). FFmpeg’s mode=rendezvous doesn’t help here (see above). Instead, set up a relay server with a public IP address (a VPS, for example) as the listener, and have both ends connect to it as callers.
FAQ
Should I choose SRT or RTMP?
SRT is better for contribution, that is, sending video from the field or a remote site to a studio or the cloud. It is also better on unstable links and when low latency matters. RTMP is still often the standard way into streaming platforms (such as YouTube Live), so use RTMP there. A common setup uses both: SRT from the field to the cloud, and RTMP from the cloud to the platform.
What value should I set for latency?
Start from about 3–4× the link’s RTT (round-trip time). On a stable link such as a LAN, the default of about 120 ms is enough. Over mobile networks or long internet paths, raising it to about 200–1000 ms (latency=200000 to 1000000) copes better with packet loss. A lower value means less delay but less stability, so find the smallest value at which the video doesn’t break up on your real link.
Can I encrypt the stream?
Yes. SRT supports AES encryption; add passphrase= to the URL to turn it on. pbkeylen= sets the key length (16/24/32 bytes, default 16). Use the same passphrase on the sender and the receiver. When sending over the internet, set a passphrase.
I want to deliver received SRT to viewers
SRT is for moving video between places (contribution and relay). To reach many viewers, the usual way is to split the received MPEG-TS into HLS or DASH segments. For the HLS steps, see the HLS segmenting article below.