目標は「カメラのライトが点滅したら、次のフレームで ピクセルが表示される」ことだ。FFmpeg のデフォルトはその用途に 調整されていない。バッファリングし、並べ替え、待機する。遅延を 抑える鍵は、いくつかのフラグと1つのコールバックだ。これらを 見逃すと数百 ms のコストになる。
フラグ
opts.cpp
AVDictionary* opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0); // または udp —
// 損失はあるが遅延は小さい
av_dict_set(&opts, "fflags", "nobuffer", 0); // AVIO 入力バッファを
// スキップ
av_dict_set(&opts, "flags", "low_delay", 0);// デコーダーフラグ:
// 並べ替えなし
av_dict_set(&opts, "max_delay", "0", 0);
av_dict_set(&opts, "stimeout", "5000000", 0); // RTSP ソケットの
// タイムアウト(µs)
av_dict_set(&opts, "reconnect", "1", 0); // http キープアライブ
int r = avformat_open_input(&fmt, url, nullptr, &opts);最重要なのは nobuffer だ。これがないと FFmpeg はフォーマットを
推測するためにプローブ用のウィンドウをバッファリングし、起動時の
遅延が増える。
実際に機能するタイムアウト — AVIOInterruptCB
interrupt.cpp
// stimeout はソケット層しかカバーしない。停滞した TLS ハンド
// シェイクやハングした TCP ストリームは依然として av_read_frame
// を永久にブロックし得る。本当のデッドラインとして機能するのは
// 割り込みコールバックだ:
static int interruptCb(void* ctx) {
auto* d = static_cast<Deadline*>(ctx);
return d->expired(); // 非ゼロ → FFmpeg はブロック中の処理を中断する
}
fmt->interrupt_callback = {interruptCb, &deadline};
deadline.arm(5000ms);
av_read_frame(fmt, pkt); // コールバックが発火すると EAGAIN/EXIT を返す
RTSP と HTTP の両方で機能する。stimeout は RTSP 専用、timeout は
HTTP 専用だが、割り込みコールバックは両方をカバーする。
デコーダー側の遅延
decode.cpp
avctx->flags |= AV_CODEC_FLAG_LOW_DELAY; // フレームの並べ替えなし
avctx->thread_count = 1; // フレームスレッディングは
// フレームをキューイング = 遅延
avctx->flags2 |= AV_CODEC_FLAG2_FAST; // 一部のチェックをスキップ
// H.264 の場合:IDR 位置に揃った出力を要求し、エンコーダーが許せば
// B フレームを落とす — B フレームの並べ替えはコーデックごとの遅延源
フレームスレッディング(thread_count > 1)は見落とされがちな遅延源だ。
デコーダーは最初のフレームを出力する前に内部で thread_count 個の
フレームをキューイングする。
エンドツーエンドの遅延予算
budget.txt
ネットワーク読み取り stimeout/interruptCb → 停滞を約100msで検出
demux fflags=nobuffer → プローブウィンドウの遅延なし
decode low_delay, threads=1 → 並べ替えキューなし、フレーム
スレッディングの滞留なし
upload/present 最新フレームのメールボックス → 古いフレームは破棄、
最新を即座に表示実践的なルール
- 信頼性には
rtsp_transport=tcp、最小遅延にはudpを選ぶ。 UDP は再送せずパケットを落とす(ライブ映像のウォールでは正しい 選択だ。破れたフレームの方が 500ms の再送停滞よりましだ)。 stimeoutは RTSP ソケットの接続+読み取りタイムアウトだ。 割り込みコールバックは HTTP パスと、タイムアウトでは拾えない デッドロックをカバーする。decode_threads = 1+nobuffer+low_delayがライブカメラ ウォールのベースラインだ。それぞれがスループットを少し犠牲にして 遅延を削る。- 実カメラでテストする。
testsrcは決して停滞しない。割り込みパスが 実際に動くのは、不安定なネットワークか実カメラのキープアライブ パターンのときだけだ。 av_read_frameの戻り値を監視する。割り込み由来のAVERROR(EAGAIN)は回復可能な停滞、AVERROR_EOFは本当のストリーム終端だ。
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください