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セグメント生成」にあります。

関連記事