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.0.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.0.1 Copyright (c) 2000-2025 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.
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:
- How to Stream HLS Video with FFmpeg and a CDN — Advanced HLS configuration
- FFmpeg GPU Acceleration Guide — Handling multiple camera streams
- FFmpeg Usage Tutorial — FFmpeg basics