SRTとは(RTMP・HLSとの違い)
SRT(Secure Reliable Transport)は、Haivisionが開発してオープンソースにした、遅延の小さい伝送プロトコルです。UDPをもとにしながら、失われたパケットの再送(ARQ:Automatic Repeat reQuest)と、AESによる暗号化に対応しています。インターネットのように品質が安定しない回線でも、TCPほど遅延を増やさずに、映像を確実に送れます。
ほかの方式との違い:
| プロトコル | トランスポート | 遅延 | ロス耐性 | 主な用途 |
|---|---|---|---|---|
| SRT | UDP(ARQ付き) | 低(数百ms〜) | 高 | 中継・コントリビューション、現場→クラウド |
| RTMP | TCP | 中 | 中(再送で遅延増) | 配信プラットフォームへの入力 |
| HLS | HTTP(TCP) | 高(数秒〜数十秒) | 高 | 視聴者への大規模配信 |
RTMPはTCPを使うため、パケットが落ちると再送のたびに遅延がたまりやすくなります。HLSは大勢の視聴者に届けるのが得意ですが、遅延が大きいので、撮影現場からスタジオやクラウドへ映像を送り込む用途(コントリビューション)には向きません。
SRTは、このコントリビューションや拠点間の中継に向けて作られています。ファイアウォールやNATを越えるための接続モードもあり、その点でもRTMPより柔軟です。視聴者への配信は、SRTで受けた映像をHLSやDASHに変換して行うのが一般的です。
前提:libsrt対応の確認
FFmpegでSRTを使うには、--enable-libsrt 付きでビルドされたFFmpegが必要です。手元のFFmpegが対応しているかは、次のコマンドで確認します。
ffmpeg -protocols | grep srt
出力に srt があれば対応しています。何も表示されなければ、そのFFmpegはlibsrtに対応していません。
srt
対応していないときは、次の方法があります。
- Linux:ディストリビューションの公式パッケージは、対応していないことがあります。staticビルド(John Van Sickle 氏のビルドなど)のように、libsrtを含むビルドを使うのが手軽です。
- macOS:Homebrewの
ffmpegはlibsrtを含みません。含んでいるのはffmpeg-fullです(brew install ffmpeg-full)。PATHには追加されないので、$(brew --prefix ffmpeg-full)/bin/ffmpegで実行します。 - Windows:gyan.dev や BtbN が配布しているビルド(full / gpl版)は、libsrtを含んでいます。
- 自分でビルドするときは、先に
libsrtをインストールし、./configure --enable-libsrtを付けます。
SRTのURLを使ったときに Protocol not found と出たら、ほぼ確実に、そのFFmpegはlibsrtに対応していません。
最小の送受信コマンド
SRTでは、中身にMPEG-TS(-f mpegts)を使うのが一般的です。SRTは映像のフレームの区切りを扱わず、データを小さなパケット(既定では1316バイトまで)に分けて運ぶだけなので、それだけで完結したコンテナであるMPEG-TSが標準になっています。
まず受信側を起動します。listenerモードで待ち受け、届いた映像をファイルに保存します。
ffmpeg -i "srt://0.0.0.0:9000?mode=listener" -c copy received.mp4
0.0.0.0 はすべてのネットワークインターフェースで待ち受ける指定で、9000 は好きに選べるUDPポートです。受信側は、送信側より先に待ち受けている必要があります。
次に、送信側(caller)から映像を送ります。
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316"
receiver-host は、受信側のホスト名かIPアドレスに置き換えます。-c copy で再エンコードせずにそのまま流し、-f mpegts でMPEG-TSに包んで送ります。
-re:入力を本来の速さ(フレームレートどおり)で読み込みます。ファイルをライブのように等速で送るには必須です。付けないとファイルを一気に読み込んでしまい、ライブの送出になりません。pkt_size=1316:1回に送るUDPパケットの中身(ペイロード)の大きさです。
受信側を mode=listener、送信側を caller(mode を書かないときの既定)にする組み合わせが、いちばん簡単です。逆に、受信側をcaller、送信側をlistenerにしてもかまいません。片方が待ち受け、もう片方が接続しに行く関係なら成り立ちます。
接続モード(caller / listener / rendezvous)
SRTでは、どちらから接続を始めるかを接続モードで決めます。URLの mode= で指定します。
| モード | 動作 | 典型的な使い方 |
|---|---|---|
caller |
相手へ接続しに行く(発信側) | 送信PCが、固定IPの受信サーバーへ送る |
listener |
接続を待ち受ける(受信側) | クラウド上の受信サーバーが待機する |
rendezvous |
双方が同時に接続を開始する | 両側がNAT/ファイアウォール内にある場合(FFmpegではこの用途に使えない。後述) |
caller と listener(いちばん基本の組み合わせ):片方を listener にして待ち受け、もう片方を caller にして接続しに行きます。接続しに行く側から、相手のグローバルIPとポートに届く必要があります。
# 受信側(listener)
ffmpeg -i "srt://0.0.0.0:9000?mode=listener" -c copy received.mp4
# 送信側(caller、mode省略時の既定)
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316"
rendezvous:両側が同時に相手へ接続を始めるモードです。ただしFFmpegの mode=rendezvous は、URLに書いたアドレスとポートを自分の側に割り当て(bind)、同じアドレスへ接続しようとします。そのため相手のアドレスを書くと、接続を始める前にエラーで止まります(Windowsでは Error number -10049 occurred)。自分の側のアドレスを別に指定するオプションもないので、両側がNATの内側にあるときは、後述のように中継サーバーを使います。
FFmpegでは、mode を省略すると、アドレスに関係なく caller になります。0.0.0.0 と書いても待ち受けないので、受信側で待ち受けるときは必ず mode=listener を書きます。
主要パラメータ(pkt_size・latency・mode)
SRTの設定は、URLの後ろに ?key=value&key2=value2 の形で付けます。
pkt_size
?pkt_size=1316
pkt_size=1316 は、1回に送るパケットの中身の大きさ(バイト)です。MPEG-TSのパケットは188バイトで、その7倍(188 × 7 = 1316)がこの値です。1316バイトなら、IP・UDP・SRTのヘッダーを足しても、標準的なイーサネットのMTU(1500バイト)に収まります。パケットが途中で分割されず(IPフラグメンテーションを避けられ)、効率よく送れるので、SRTやUDPで広く推奨されています。MPEG-TSをSRTで送るなら、pkt_size=1316 を付けておけば問題ありません。
latency
?latency=200000
latency= は、受信側がパケットの再送を待つための猶予時間(SRTの受信バッファ)です。FFmpegでは単位がほかのSRTツールのようなミリ秒ではなくマイクロ秒なので、200msなら latency=200000 と書きます。回線でパケットが落ちたとき、SRTはこの時間の範囲で再送(ARQ)を行い、欠けた分を埋めます。
- 大きくすると再送が間に合いやすくなり、ロスの多い不安定な回線でも映像が崩れにくくなります。そのかわり、全体の遅延は増えます。
- 小さくすると遅延は減りますが、パケットロスに弱くなります。
- 既定値は120ms前後です。RTT(データが往復するのにかかる時間)の3〜4倍程度を目安に、回線の品質に合わせて調整します。
不安定な回線に向けて、猶予時間を指定する例:
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316&latency=200000"
複数の設定を並べるときは & でつなぎます。
mode
どちらから接続するかを決めます(caller / listener / rendezvous)。両側の組み合わせが合っていないと接続できないので、必ずペアで合わせます。
再エンコードして送出する
-c copy は素材をそのまま流すので、いちばん軽い方法です。ただ、入力のコーデックやキーフレームの入り方(GOP)が送り先の条件に合わないときは、再エンコードします。たとえば、受信側がH.264 + AACのMPEG-TSを前提にしているときや、キーフレームの間隔を一定にしたいときです。
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"
各オプションの意味:
| オプション | 役割 |
|---|---|
-c:v libx264 |
映像をH.264で再エンコード(広く再生可能) |
-preset veryfast |
ライブ送出向けに低遅延・低CPU負荷のプリセット |
-b:v 4000k / -maxrate 4000k / -bufsize 8000k |
映像のビットレートの目標を4Mbpsにする。キーフレームなどで一瞬4Mbpsを超えることはあるが、長くは超えない(単純な映像では下がる) |
-pix_fmt yuv420p |
互換性の高いピクセルフォーマットに統一 |
-g 60 |
キーフレームの間隔を最大60フレームにする(30fpsなら最大2秒)。間隔を一定にするには -sc_threshold 0 も付ける |
-c:a aac -b:a 128k |
音声をAAC 128kbpsで再エンコード |
-maxrate と -bufsize を付けると、映像のビットレートが4Mbpsを長く超えることはなくなります。ただし、キーフレームなどで一瞬だけ大きく超えることはあり、音声やMPEG-TS・SRTのデータも上乗せされます。回線の帯域には余裕を残してください。-g 60 でキーフレームを定期的に入れておくと、受信側がつなぎ直したときに早く映像が戻ります。CPUに余裕がないときは -preset ultrafast でさらに負荷を下げられますが、同じビットレートでの画質は落ちます。
よくあるエラーと対処
Protocol not found
FFmpegがlibsrtなしでビルドされていると出ます。ffmpeg -protocols | grep srt で確認し、何も出なければ、libsrtを含むビルド(Homebrewの ffmpeg-full、gyan.devのfull版、BtbNのビルド、staticビルドなど)に入れ替えます。
接続できない(タイムアウト前に確立しない)
よくある原因は次の3つです。
- ポートが開いていない:SRTはUDPを使います。受信側のファイアウォールやセキュリティグループで、そのポート(例: 9000)のUDPの受信を許可します。TCPだけ開けても通りません。
- modeの組み合わせが違う:片方が
caller、もう片方がlistenerになっている必要があります。両方がcaller、または両方がlistenerだと、いつまでもつながりません。 - 起動の順番:先にlistener側を待ち受けの状態にしてから、caller側を起動します。
パケットロスで映像が乱れる
ブロックノイズや途切れが出るのは、回線のパケットロスに対して、再送を待つ時間が足りていないからです。latency を増やすと改善します(500msなら latency=500000)。
ffmpeg -re -i input.mp4 -c copy -f mpegts "srt://receiver-host:9000?pkt_size=1316&latency=500000"
それでも直らないときは、回線の帯域に対してビットレートが高すぎるのかもしれません。再エンコードして送り、-maxrate を下げて帯域に余裕を持たせます。
両側がNATの内側にあってつながらない
両側がそれぞれNATの内側にあると、callerからlistenerに直接届かず、接続できません(FFmpeg 8.1では Connection to srt://... failed: I/O error と出ます)。FFmpegの mode=rendezvous はこの場合には使えないので(前述)、グローバルIPを持つ中継サーバー(VPSなど)をlistenerとして用意し、両側からそこへcallerとして接続します。
よくある質問
SRTとRTMP、どちらを選ぶべき?
SRTが有利なのは、撮影現場や離れた拠点からスタジオやクラウドへ映像を送るとき(コントリビューション)です。回線が不安定なときや、遅延を小さくしたいときもSRTが向いています。配信サービス(YouTube Liveなど)への入力は、まだRTMPが標準のことも多いので、その場合はRTMPを使います。現場からクラウドまではSRT、クラウドから配信サービスまではRTMP、と組み合わせる構成もよくあります。
latencyはどのくらいに設定すればいい?
回線のRTT(往復にかかる時間)の3〜4倍が目安です。LANのように安定した回線なら、既定の120ms前後で足ります。モバイル回線や、遠く離れた相手とのインターネット経由なら、200〜1000ms程度(latency=200000〜1000000)に増やすとパケットロスに強くなります。遅延と安定性は、片方をよくすればもう片方が悪くなる関係です。実際の回線で、映像が崩れない最小の値を探します。
暗号化はできる?
できます。SRTはAESによる暗号化に対応していて、URLに passphrase= を付けると有効になります。鍵の長さは pbkeylen=(16/24/32バイト)で選べ、省略すると16です。送信側と受信側で、同じパスフレーズを指定します。インターネット越しに送るなら、パスフレーズを設定してください。
受信したSRTを視聴者向けに配信したい
SRTは、映像を送り込んだり中継したりするためのプロトコルです。大勢の視聴者に届けるには、受け取ったMPEG-TSをHLSやDASHのセグメントに分けて配信するのが定番です。HLSにする手順は、関連記事の「HLSセグメント生成」にあります。