IPカメラの映像をブラウザで見たい。録画したい。複数のカメラを1つの画面にまとめたい——こういったニーズに応えるのが、FFmpegのRTSP対応機能だ。
RTSP(Real-Time Streaming Protocol)はIPカメラのデファクトスタンダードだが、ブラウザは直接RTSPを再生できない。FFmpegを間に挟んでHLS(HTTP Live Streaming)に変換すれば、どんなデバイスのブラウザでもカメラ映像を表示できる。
この記事では、RTSPの仕組みの理解から始めて、FFmpegでの受信・録画・HLS変換・トラブルシューティングまでを一通り解説する。
RTSPとは何か — プロトコルの仕組みを図解
RTSP(Real-Time Streaming Protocol)は、ネットワーク上でメディアストリームを制御するためのプロトコルだ。RFC 2326(1998年)で標準化され、後にRFC 7826(RTSP 2.0、2016年)で改訂されている。
ポイントは「RTSPはメディアデータそのものを運ばない」ということだ。RTSPの役割はリモコンのようなもので、PLAY・PAUSE・TEARDOWNといった制御コマンドを送る。実際の映像・音声データはRTP(Real-time Transport Protocol)で別途配信される。
このやり取りの流れを整理すると:
- DESCRIBE: クライアントがサーバーに「何のストリームがあるか」を問い合わせる。サーバーはSDP(Session Description Protocol)で映像のコーデック、解像度、ポート番号などを返す
- SETUP: 使用するトランスポート(UDP / TCP)を決定する
- PLAY: ストリームの送信を開始する。ここからRTPでデータが流れ始める
- TEARDOWN: ストリームを終了する
RTSPのURLフォーマット
カメラメーカーごとにURLの形式が異なる。主要メーカーのパターンを押さえておこう。
# Hikvision — チャンネル101がメインストリーム、102がサブストリーム
rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101
# Dahua — channel=1&subtype=0 がメインストリーム
rtsp://admin:password@192.168.1.108:554/cam/realmonitor?channel=1&subtype=0
# ONVIF汎用 — 多くのカメラが対応
rtsp://admin:password@192.168.1.100:554/onvif1
# Tapo(TP-Link)— stream1がメイン、stream2がサブ
rtsp://admin:password@192.168.1.200:554/stream1
# USBカメラ(v4l2経由・Linux)— RTSPではないがFFmpegで直接取得可能
# /dev/video0
環境準備 — FFmpeg 8.0.1のRTSP対応確認
FFmpegのRTSP対応は標準でビルドに含まれている。特別なオプションは不要だが、確認しておくと安心だ。
# FFmpegのバージョン確認
ffmpeg -version
# ffmpeg version 8.0.1 Copyright (c) 2000-2025 the FFmpeg developers
# RTSPプロトコルが有効か確認
ffmpeg -protocols 2>/dev/null | grep rtsp
# Input: rtsp
# Output: rtsp
# 利用可能なデコーダー確認(H.264は必須)
ffmpeg -decoders 2>/dev/null | grep h264
# V..... h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
3つとも出力が確認できれば準備完了だ。
接続テスト
いきなりFFmpegで変換する前に、まずストリームが正常に取得できるか確認しよう。ffprobe を使えばストリームの情報を確認できる。
ffprobe -rtsp_transport tcp \
"rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101"
成功すると、以下のような情報が表示される。
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
-rtsp_transport tcp を指定している理由は後述する。ここでストリーム情報が取れない場合は「よくあるエラーと解決策」のセクションを参照してほしい。
RTSPストリームを受信して録画する
まずは最もシンプルなケース——RTSPストリームをそのままファイルに保存する方法だ。
コーデックを変換せずに保存する(-c copy)
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy \
-t 60 \
output.mp4
| オプション | 意味 |
|---|---|
-rtsp_transport tcp | RTSPのデータ転送にTCPを使用する(後述) |
-i "rtsp://..." | 入力ソースのRTSP URL |
-c copy | 映像・音声を再エンコードせずそのままコピー(CPU負荷ほぼゼロ) |
-t 60 | 60秒間録画して自動停止 |
-c copy はIPカメラからのH.264/H.265ストリームをそのまま保存するため、映像品質は劣化しない。CPU負荷もほぼゼロだ。各オプションの詳しい解説は FFmpegの使い方ガイド にまとめている。
時間指定なしで録画する(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
-movflags +faststart を付けると、moatom(メタデータ)がファイル先頭に移動し、録画途中でもブラウザで再生可能になる。ただし Ctrl+C で正常終了しないとファイルが壊れる可能性がある。
セグメント分割で連続録画する
24時間連続録画する場合、1つの巨大ファイルにするのは現実的ではない。-f segment で時間ごとにファイルを分割しよう。
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"
これで1時間(3600秒)ごとに cam01_20260312_140000.mp4 のようなファイル名で自動分割される。-strftime 1 がファイル名に日時を埋め込むオプションだ。
RTSP → HLSにリアルタイム変換する
録画だけでなく、カメラ映像をリアルタイムにブラウザで見たい場合はHLS変換が必要だ。HLS(HTTP Live Streaming)は映像を短いセグメントに分割し、HTTPで配信するプロトコルで、ほぼすべてのブラウザで再生できる。HLSの詳しい仕組みは FFmpegでHLS動画をCDN配信する方法 で解説している。
全体のデータフローはこうなる:
基本的なHLS変換コマンド
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"
| オプション | 意味 |
|---|---|
-c:v copy | 映像はカメラのH.264をそのまま使う(再エンコードなし) |
-c:a aac | 音声はAACに変換(ブラウザ互換性のため) |
-f hls | 出力フォーマットをHLSに指定 |
-hls_time 2 | 各セグメントの長さを2秒に設定 |
-hls_list_size 10 | プレイリストに保持するセグメント数(10個 = 直近20秒) |
-hls_flags delete_segments | 古いセグメントファイルを自動削除(ディスク節約) |
-hls_flags append_list | プレイリストを上書きではなく追記モードにする |
生成される index.m3u8 をWebサーバー(Nginx等)で配信すれば、ブラウザからカメラ映像が見られる。
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 "*";
}
}
ブラウザで http://サーバーIP:8080/hls/cam01/index.m3u8 にアクセスすれば映像が表示される。Safariはネイティブ対応、Chrome/Firefoxは hls.js などのライブラリが必要だ。
再エンコードが必要なケース
カメラがH.265(HEVC)で出力している場合、ブラウザの対応状況が限られる。H.264に変換してから配信するのが安全だ。
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"
-preset ultrafast -tune zerolatency は遅延を最小化する設定だ。画質よりもリアルタイム性を優先する監視カメラ用途に適している。CPU負荷が問題になる場合は GPUエンコードガイド を参照してほしい。NVIDIA GPUがあれば -c:v h264_nvenc で大幅に負荷を下げられる。
よくあるエラーと解決策
RTSPストリーミングで遭遇しやすいエラーとその対処法をまとめた。
Connection refused / Connection timed out
rtsp://192.168.1.64:554/...: Connection refused
原因: カメラのRTSPサービスが無効、ポート番号が違う、ファイアウォールでブロックされている。
対処:
# ポートが開いているか確認
nc -zv 192.168.1.64 554
# ファイアウォールを確認(Linux)
sudo iptables -L -n | grep 554
# カメラの管理画面でRTSPが有効になっているか確認
# Hikvision: 設定 → ネットワーク → 詳細設定 → ポート → RTSPポート
401 Unauthorized
Server returned 401 Unauthorized (all attempts)
原因: ユーザー名またはパスワードが間違っている。
対処: カメラの管理画面でRTSP用のアカウントを確認する。多くのカメラではWeb管理画面と同じ認証情報だが、別途設定が必要な機種もある。URLエンコードが必要な特殊文字(@, :, / 等)がパスワードに含まれていないかも確認しよう。
UDP packet loss / 映像が途切れる
[rtsp @ 0x...] RTP: dropping old packet received too late
原因: UDPで転送中にパケットが欠落している。WiFi環境やルーター越しの接続で頻発する。
対処: TCPモードに切り替える。
# UDPがデフォルト → TCPに変更
ffmpeg -rtsp_transport tcp \
-i "rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101" \
-c copy output.mp4
-rtsp_transport tcp を指定すると、RTSPのデータ転送もTCPインターリーブモード(RTP over TCP)になる。パケットロスがなくなる代わりに、わずかに遅延が増える。監視カメラ用途ではTCPを推奨する。
broken pipe / ストリームが突然切断される
原因: カメラ側がタイムアウトで接続を切った、またはネットワークが不安定。
対処: 自動再接続のラッパースクリプトを書く。
#!/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
-timeout 5000000(5秒、マイクロ秒単位)はRTSPソケットのタイムアウトだ。カメラが応答しなくなったらFFmpegを終了させ、while true ループで再起動する。本番環境ではsystemdサービスにして管理するとよい。
パフォーマンス最適化とGPU活用
CPU負荷を抑える基本戦略
-c copy を使えば再エンコードが不要でCPU負荷はほぼゼロだが、H.265→H.264変換や解像度変更が必要な場合はCPUを消費する。
# CPU使用率の確認(FFmpeg実行中に別ターミナルで)
top -p $(pgrep -f ffmpeg)
負荷軽減のポイント:
-preset ultrafastを使う(画質は若干落ちるが速度は最速)- 解像度をカメラ側で下げる(サブストリームを使う:
/Channels/102) -r 15でフレームレートを下げる(監視用途なら15fpsで十分)
GPUエンコードの活用
NVIDIA GPUがある環境なら、NVENCを使ってCPU負荷をほぼゼロにできる。
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"
-preset p4 はNVENCのバランス設定、-tune ll は低遅延チューニングだ。GPUエンコードの詳しいセットアップと各GPUメーカーの比較は FFmpegをGPUで高速化する完全ガイド を参照してほしい。
動画圧縮の最適化
帯域幅やストレージが限られている場合は、CRF値やビットレートの調整で圧縮率をコントロールできる。詳しい設定方法は FFmpeg動画圧縮の完全ガイド にまとめている。
まとめ
RTSPはIPカメラの標準プロトコルだが、ブラウザでは直接再生できない。FFmpegを使えば:
- 録画:
-c copyでCPU負荷ゼロの保存 - HLS変換:
-f hlsでブラウザ再生可能な形式にリアルタイム変換 - 安定運用: TCPモード + 自動再接続スクリプト
主要なカメラメーカー(Hikvision、Dahua、ONVIF対応機)のRTSP URLパターンも押さえた。トラブルが起きたときは、まず ffprobe で接続確認、次にTCPモードへの切り替えを試そう。
次のステップとして、複数カメラの統合やVPNでの安全なリモートアクセスに興味があれば、以下の記事も参考にしてほしい。
- FFmpegでHLS動画をCDN配信する方法 — HLS配信の応用設定
- FFmpegをGPUで高速化する完全ガイド — 複数カメラ処理の負荷対策
- FFmpegの使い方 完全ガイド — FFmpegの基本操作