32blogby StudioMitsu

How to Receive, Convert, and Stream RTSP Camera Feeds with FFmpeg

Learn how to receive, record, and convert RTSP streams to HLS with FFmpeg 8.0. Covers protocol fundamentals, troubleshooting, and GPU acceleration.

10 min read
On this page

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).

RTSP (TCP:554)
Client(FFmpeg)
DESCRIBE
SDP Response
SETUP
PLAY
RTP (video)
RTCP (ctrl)
Camera(Server)

The exchange flow works like this:

  1. 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.
  2. SETUP: Negotiate the transport method (UDP or TCP)
  3. PLAY: Start streaming. RTP data begins flowing
  4. TEARDOWN: End the session

RTSP URL Formats

Each camera manufacturer uses a different URL format. Here are the patterns for major brands:

bash
# 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.

bash
# 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.

bash
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)

bash
ffmpeg -rtsp_transport tcp \
  -i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
  -c copy \
  -t 60 \
  output.mp4
OptionPurpose
-rtsp_transport tcpUse TCP for RTSP data transfer (explained later)
-i "rtsp://..."Input source RTSP URL
-c copyCopy video and audio without re-encoding (near-zero CPU usage)
-t 60Record 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)

bash
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.

bash
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:

IP CameraRTSP ServerRTSPFFmpegTranscodeWriteHLS Files.m3u8 + .tsHTTPBrowserhls.js

Basic HLS Conversion Command

bash
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"
OptionPurpose
-c:v copyPass through the camera's H.264 video without re-encoding
-c:a aacConvert audio to AAC for browser compatibility
-f hlsSet output format to HLS
-hls_time 2Set each segment duration to 2 seconds
-hls_list_size 10Keep 10 segments in the playlist (20 seconds of recent footage)
-hls_flags delete_segmentsAuto-delete old segment files to save disk space
-hls_flags append_listAppend 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

nginx
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.

bash
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:

bash
# 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.

bash
# 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.

bash
#!/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.

bash
# 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.

bash
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 copy at near-zero CPU usage
  • Convert to HLS: Real-time conversion with -f hls for 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: