You want to view your IP camera feed in a browser. Record it. Maybe combine multiple cameras into a single dashboard. FFmpeg's RTSP support makes all of this possible.
RTSP (Real-Time Streaming Protocol) is the de facto standard for IP cameras, but browsers cannot play RTSP streams directly. By using FFmpeg as a middleman to convert RTSP to HLS (HTTP Live Streaming), you can display camera feeds on any device with a browser.
This article walks you through the RTSP protocol fundamentals, then covers receiving, recording, and converting streams with FFmpeg — including troubleshooting and performance optimization.
What Is RTSP — Protocol Fundamentals Explained
RTSP (Real-Time Streaming Protocol) is a protocol for controlling media streams over a network. It was standardized in RFC 2326 (1998) and later revised in RFC 7826 (RTSP 2.0, 2016).
The key insight is that RTSP does not carry the media data itself. Think of RTSP as a remote control — it sends commands like PLAY, PAUSE, and TEARDOWN. The actual video and audio data flows separately via RTP (Real-time Transport Protocol).
The exchange flow works like this:
- DESCRIBE: The client asks the server what streams are available. The server responds with SDP (Session Description Protocol) containing the codec, resolution, port numbers, etc.
- SETUP: Negotiate the transport method (UDP or TCP)
- PLAY: Start streaming. RTP data begins flowing
- TEARDOWN: End the session
RTSP URL Formats
Each camera manufacturer uses a different URL format. Here are the patterns for major brands:
# Hikvision — Channel 101 is the main stream, 102 is the sub stream
rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101
# Dahua — channel=1&subtype=0 is the main stream
rtsp://admin:password@192.168.1.108:554/cam/realmonitor?channel=1&subtype=0
# ONVIF generic — most cameras support this
rtsp://admin:password@192.168.1.100:554/onvif1
# Tapo (TP-Link) — stream1 is main, stream2 is sub
rtsp://admin:password@192.168.1.200:554/stream1
# USB camera (v4l2 on Linux) — not RTSP but FFmpeg can capture directly
# /dev/video0
Environment Setup — Verifying FFmpeg 8.1 RTSP Support
RTSP support is included in standard FFmpeg builds. No special compile flags are needed, but let's verify.
# Check FFmpeg version
ffmpeg -version
# ffmpeg version 8.1 Copyright (c) 2000-2026 the FFmpeg developers
# Verify RTSP protocol is available
ffmpeg -protocols 2>/dev/null | grep rtsp
# Input: rtsp
# Output: rtsp
# Verify H.264 decoder is available
ffmpeg -decoders 2>/dev/null | grep h264
# V..... h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
If all three commands produce output, you're ready to go.
Connection Test
Before attempting any conversion, verify that the stream is accessible using ffprobe.
ffprobe -rtsp_transport tcp \
"rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101"
A successful response looks like this:
Input #0, rtsp, from 'rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101':
Duration: N/A, start: 0.000000, bitrate: N/A
Stream #0:0: Video: h264 (Main), yuv420p, 1920x1080, 25 fps
Stream #0:1: Audio: aac (LC), 16000 Hz, mono, fltp
The -rtsp_transport tcp flag is explained later. If you can't get stream information here, jump to the "Common Errors and How to Fix Them" section.
Receiving and Recording an RTSP Stream
Let's start with the simplest case — saving an RTSP stream directly to a file.
Saving Without Re-encoding (-c copy)
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy \
-t 60 \
output.mp4
| Option | Purpose |
|---|---|
-rtsp_transport tcp | Use TCP for RTSP data transfer (explained later) |
-i "rtsp://..." | Input source RTSP URL |
-c copy | Copy video and audio without re-encoding (near-zero CPU usage) |
-t 60 | Record for 60 seconds then stop |
The -c copy flag preserves the original H.264/H.265 stream from the camera with no quality loss and almost no CPU usage. For a detailed explanation of each option, see the FFmpeg Usage Tutorial.
Recording Indefinitely (Stop with Ctrl+C)
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy \
-movflags +faststart \
recording_$(date +%Y%m%d_%H%M%S).mp4
The -movflags +faststart flag moves the moov atom (metadata) to the beginning of the file, making it playable in browsers even during recording. However, if FFmpeg is not terminated cleanly (via Ctrl+C), the file may be corrupted.
Segmented Continuous Recording
For 24/7 recording, a single large file is impractical. Use -f segment to split by time.
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy \
-f segment \
-segment_time 3600 \
-segment_format mp4 \
-reset_timestamps 1 \
-strftime 1 \
"recordings/cam01_%Y%m%d_%H%M%S.mp4"
This creates a new file every hour (3600 seconds) with names like cam01_20260312_140000.mp4. The -strftime 1 flag enables timestamp-based filenames.
Converting RTSP to HLS in Real Time
When you want to view camera feeds live in a browser, you need HLS conversion. HLS (HTTP Live Streaming) splits video into short segments served over HTTP, and is supported by virtually all browsers. For a deep dive into HLS, see How to Stream HLS Video with FFmpeg and a CDN.
Here is the overall data flow:
Basic HLS Conversion Command
mkdir -p /var/www/hls/cam01
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c:v copy \
-c:a aac -b:a 128k \
-f hls \
-hls_time 2 \
-hls_list_size 10 \
-hls_flags delete_segments+append_list \
-hls_segment_filename "/var/www/hls/cam01/seg_%03d.ts" \
"/var/www/hls/cam01/index.m3u8"
| Option | Purpose |
|---|---|
-c:v copy | Pass through the camera's H.264 video without re-encoding |
-c:a aac | Convert audio to AAC for browser compatibility |
-f hls | Set output format to HLS |
-hls_time 2 | Set each segment duration to 2 seconds |
-hls_list_size 10 | Keep 10 segments in the playlist (20 seconds of recent footage) |
-hls_flags delete_segments | Auto-delete old segment files to save disk space |
-hls_flags append_list | Append to the playlist instead of overwriting |
Serve the generated index.m3u8 via a web server (like Nginx) and you can view the camera feed in a browser.
Nginx Configuration
server {
listen 8080;
server_name localhost;
location /hls/ {
alias /var/www/hls/;
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
add_header Cache-Control "no-cache";
add_header Access-Control-Allow-Origin "*";
}
}
Navigate to http://server-ip:8080/hls/cam01/index.m3u8 in your browser. Safari supports HLS natively; Chrome and Firefox need a library like hls.js.
When Re-encoding Is Necessary
If your camera outputs H.265 (HEVC), browser support is limited. Transcoding to H.264 is the safe choice.
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c:v libx264 -preset ultrafast -tune zerolatency -crf 23 \
-c:a aac -b:a 128k \
-f hls \
-hls_time 2 \
-hls_list_size 10 \
-hls_flags delete_segments+append_list \
"/var/www/hls/cam01/index.m3u8"
The -preset ultrafast -tune zerolatency combination minimizes latency, prioritizing real-time delivery over compression efficiency — ideal for surveillance. If CPU load is a concern, see the GPU Encoding Guide for hardware-accelerated alternatives.
Common Errors and How to Fix Them
Here are the most frequent issues you'll encounter with RTSP streaming and how to resolve them.
Connection refused / Connection timed out
rtsp://192.168.1.64:554/...: Connection refused
Cause: The camera's RTSP service is disabled, the port number is wrong, or a firewall is blocking the connection.
Fix:
# Check if the port is open
nc -zv 192.168.1.64 554
# Check firewall rules (Linux)
sudo iptables -L -n | grep 554
# Verify RTSP is enabled in the camera's admin panel
# Hikvision: Configuration → Network → Advanced → Port → RTSP Port
401 Unauthorized
Server returned 401 Unauthorized (all attempts)
Cause: Wrong username or password.
Fix: Check the RTSP credentials in your camera's admin panel. Most cameras use the same credentials as the web interface, but some require separate RTSP accounts. Also verify that special characters (@, :, /) in the password are properly URL-encoded.
UDP Packet Loss / Choppy Video
[rtsp @ 0x...] RTP: dropping old packet received too late
Cause: UDP packets are being lost during transfer. This is common over WiFi or when routing through multiple network hops.
Fix: Switch to TCP mode.
# UDP is the default — switch to TCP
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy output.mp4
The -rtsp_transport tcp flag switches to TCP interleaved mode (RTP over TCP). This eliminates packet loss at the cost of slightly higher latency. TCP is recommended for surveillance use cases.
Broken Pipe / Stream Suddenly Disconnects
Cause: The camera closed the connection due to a timeout, or the network is unstable.
Fix: Write an auto-reconnect wrapper script.
#!/bin/bash
RTSP_URL="rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101"
OUTPUT_DIR="/var/www/hls/cam01"
while true; do
echo "[$(date)] Starting FFmpeg..."
ffmpeg -rtsp_transport tcp \
-timeout 5000000 \
-i "$RTSP_URL" \
-c:v copy -c:a aac \
-f hls \
-hls_time 2 \
-hls_list_size 10 \
-hls_flags delete_segments+append_list \
-hls_segment_filename "$OUTPUT_DIR/seg_%03d.ts" \
"$OUTPUT_DIR/index.m3u8"
echo "[$(date)] FFmpeg exited with code $?. Restarting in 5 seconds..."
sleep 5
done
The -timeout 5000000 (5 seconds, in microseconds) sets the RTSP socket timeout. When the camera stops responding, FFmpeg exits and the while true loop restarts it. For production, consider running this as a systemd service.
Performance Optimization and GPU Acceleration
Reducing CPU Load
With -c copy, there's no re-encoding and CPU usage is near zero. But if you need H.265→H.264 conversion or resolution changes, CPU consumption increases significantly.
# Monitor CPU usage while FFmpeg is running (in a separate terminal)
top -p $(pgrep -f ffmpeg)
Tips for reducing load:
- Use
-preset ultrafast(slight quality loss but maximum speed) - Lower the resolution on the camera side (use the sub stream:
/Channels/102) - Reduce frame rate with
-r 15(15 fps is sufficient for surveillance)
GPU Acceleration
If you have an NVIDIA GPU, NVENC can offload encoding from the CPU almost entirely.
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c:v h264_nvenc -preset p4 -tune ll \
-c:a aac -b:a 128k \
-f hls \
-hls_time 2 \
-hls_list_size 10 \
-hls_flags delete_segments+append_list \
"/var/www/hls/cam01/index.m3u8"
The -preset p4 is NVENC's balanced preset, and -tune ll enables low-latency tuning. For detailed GPU encoding setup and comparisons across GPU vendors, see the FFmpeg GPU Acceleration Guide.
Optimizing Video Compression
When bandwidth or storage is limited, adjusting CRF values and bitrate settings can help control compression. See the FFmpeg Video Compression Guide for detailed configuration.
FAQ
What is the difference between RTSP and HLS?
RTSP (Real-Time Streaming Protocol) is a control protocol used by IP cameras to deliver live video over a local network, typically via RTP for the actual media data. HLS (HTTP Live Streaming) splits video into short segments served over standard HTTP, making it playable in any browser. FFmpeg bridges the gap by converting RTSP streams to HLS in real time.
Can I view RTSP streams directly in a browser?
No. Browsers do not support the RTSP protocol natively. You need a middleware like FFmpeg to convert the RTSP stream to a browser-compatible format such as HLS. Once converted, you can serve the HLS playlist via a web server like Nginx and play it using hls.js in Chrome/Firefox or natively in Safari.
Should I use UDP or TCP for RTSP transport?
Use TCP (-rtsp_transport tcp) for most cases, especially surveillance. UDP is the default and has lower latency, but it's prone to packet loss over WiFi or across network hops, which causes choppy video. TCP guarantees packet delivery at the cost of slightly higher latency — a worthwhile trade-off for reliable recording and streaming.
How do I find my camera's RTSP URL?
Check your camera manufacturer's documentation. Common patterns: Hikvision uses rtsp://user:pass@IP:554/Streaming/Channels/101, Dahua uses rtsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0. For ONVIF-compatible cameras, try rtsp://user:pass@IP:554/onvif1. You can also use tools like ONVIF Device Manager to discover RTSP endpoints automatically.
Why does my RTSP stream keep disconnecting?
Common causes include camera-side idle timeouts, network instability, or firewall interference. Use the -timeout 5000000 flag (5 seconds in microseconds) so FFmpeg exits cleanly on timeout, then wrap the command in a reconnect loop. For production, run the reconnect script as a systemd service for automatic restart on failure.
Can FFmpeg handle multiple RTSP streams simultaneously?
Yes. Run one FFmpeg process per camera stream. A typical 4-core server can handle 8-16 streams with -c copy (no re-encoding). If transcoding is needed, GPU acceleration with NVENC can handle 10+ simultaneous streams. For a complete multi-camera setup, see Building a Multi-Camera Surveillance Dashboard.
What is the typical latency when converting RTSP to HLS?
With default HLS settings (-hls_time 2, -hls_list_size 10), expect 4-8 seconds of latency (2-3 segments of buffering plus network delay). You can reduce this to 2-4 seconds by setting -hls_time 1 and using -hls_flags low_latency, but shorter segments increase server load. For sub-second latency, consider WebRTC instead of HLS.
Is RTSP encrypted? How do I secure my camera feeds?
Standard RTSP (port 554) transmits video unencrypted. Some cameras support RTSPS (RTSP over TLS, port 322), but adoption is limited. For secure remote access, the recommended approach is to keep cameras on an isolated VLAN and tunnel access through a VPN like WireGuard rather than exposing RTSP ports to the internet.
Wrapping Up
RTSP is the standard protocol for IP cameras, but browsers can't play it directly. With FFmpeg, you can:
- Record: Save streams with
-c copyat near-zero CPU usage - Convert to HLS: Real-time conversion with
-f hlsfor browser playback - Run reliably: TCP mode + auto-reconnect scripts for stable operation
We covered RTSP URL patterns for major camera manufacturers (Hikvision, Dahua, ONVIF-compatible devices). When things go wrong, start with ffprobe to verify connectivity, then try switching to TCP mode.
For next steps — multi-camera integration and secure remote access via VPN — check out these related articles:
- Building a Multi-Camera Surveillance Dashboard — Browser-based multi-camera grid with Node.js
- How to Stream HLS Video with FFmpeg and a CDN — Advanced HLS configuration
- FFmpeg GPU Acceleration Guide — Handling multiple camera streams
- Automate FFmpeg with Python — Batch processing for recorded footage
- FFmpeg Usage Tutorial — FFmpeg basics