OPENCV_FFMPEG_READ_ATTEMPTSで長尺動画の取りこぼしを抑える
OPENCV_FFMPEG_READ_ATTEMPTSとは、次のフレームを見つけるまでに試行する「パケット読み」の最大回数を制御する環境変数である。
事象の概要
長い動画をPOSTしてサーバ側コンテナでフレーム処理を行うと、途中でコンテナが落ちる(強制終了)ことがありました。ログには以下のH.264関連メッセージが繰り返し出ます。
[h264 @ 0x56522b9b3ac0] out of range intra chroma pred mode
[h264 @ 0x56522b9b3ac0] error while decoding MB 117 31
特徴:
- 長尺かつH.264で発生しやすい
- 同一ファイルでも再現が不安定(タイミング依存)
- OpenCVの
VideoCapture
(FFmpegバックエンド)で読み込み
なぜ落ちるのか(作業仮説)
H.264のビットストリームに破損や欠損があると、デコーダは警告を出しつつ復旧を試みます。さらにコンテナには音声やメタデータも混在するため、しばらく映像パケットに遭遇できない区間が生じます。
OpenCVのFFmpegバックエンドは「次の映像フレームを見つけるため、上限回数までパケットを読む」という挙動です。上限が小さい場合、
-
映像に届く前に諦める→
read()
がFalse、アプリがリトライや再オープンを多発 - 結果として内部キュー膨張や例外ハンドリング漏れが誘発され、CPUスパイクやメモリ逼迫からコンテナ落ち(OOM Killや監視タイムアウト)
OPENCV_FFMPEG_READ_ATTEMPTS
を十分大きくすると、FFmpegが粘って読み進め映像パケットに到達できる確率が上がり、無駄な再試行が減ってコンテナの安定性が向上しました。
※ 上記は現象に合う作業仮説です。根本要因は入力品質や再試行設計など複合です。
OPENCV_FFMPEG_READ_ATTEMPTSの意味と効きどころ
-
何を制御するか
FFmpegが次の映像フレームに出会うまでのパケット読み試行の上限を指定。 -
どこに効くか
デマルチプレクサ/デコーダ前後のパケット供給ループ。映像に辿り着くまでの「気の長さ」を伸ばす。 -
トレードオフ
値を上げると復旧に強くなる一方、壊れた入力では待ち時間が伸びる可能性。監視タイムアウトやワーカ数と併せて調整。
設定方法(コンテナ運用)
Dockerfile
ENV OPENCV_FFMPEG_READ_ATTEMPTS=65536
Kubernetes (Deployment)
env:
- name: OPENCV_FFMPEG_READ_ATTEMPTS
value: "65536"
アプリ側(Python)
環境変数はcv2の読み込み前に設定。コンテナではDockerfileや起動時に設定するのが確実。
import os
os.environ["OPENCV_FFMPEG_READ_ATTEMPTS"] = "65536"
import cv2
cap = cv2.VideoCapture("input.mp4", cv2.CAP_FFMPEG)
while True:
ok, frame = cap.read()
if not ok:
break
# ここでフレーム処理
cap.release()
比較:設定前後の挙動(概念比較)
観点 | 設定前(小さい値) | 設定後(大きい値) |
---|---|---|
映像パケットまでの道のり | 途中で打ち切り | 粘って読み進める |
read() の安定性 |
Falseが散発、再オープン多発 | Trueが継続しやすい |
デコーダ警告 | 復旧できず停止しがち | 出ても前進しやすい |
リソース消費 | 再試行ループで肥大化 | 連続処理で平準化 |
コンテナ健全性 | OOM/タイムアウトで落ちやすい | 安定しやすい |
併用・代替策との比較
アプローチ | 目的 | 長所 | 注意点 |
---|---|---|---|
読み試行回数を上げる(本件) | 映像到達性の向上 | 実装変更不要、最短 | 壊れた入力で待ち増 |
再パッケージ(映像だけ残す) | 余計なトラック除去 | 読み取り軽量化 | 事前処理コスト |
再エンコード(例:H.264統一) | デコード互換性確保 | 復旧しやすい | 品質劣化/CPU負荷 |
probesize/analyzeduration調整 | 初期解析強化 | 先頭の頑健さ | ケース依存 |
分割アップロード/チャンク処理 | 長尺を分割 | メモリ安定 | 実装複雑化 |
現場で効いた理由の要約
- 長尺・多トラック・軽度破損のため映像まで遠かった。
- 既定回数が小さく探索打ち切り→
read()
失敗→再試行連鎖。 - 上限を引き上げ、FFmpegが読み進められるようにした結果、連続供給が回復。
- 再オープン多発が止まり、コンテナ落ちが消失。
実務的な設定ガイド
- まずは65536程度を起点に、監視タイムアウトや併走数と合わせて系としての安定性を見る。
- 入力品質向上(アップロード時の簡易検証、サーバ側再パッケージ)でさらに安定。
-
read()
がFalseのときは無限再試行しない。バックオフ、最大リトライ、メトリクス記録を入れる。
トラブルシュートのチェックリスト
- 入力ファイルのコーデック/トラック構成を確認。
- 短尺で再現するか(長尺依存の切り分け)。
-
read()
失敗時の再オープン回数/キュー滞留/スレッド数を計測。 - 監視のタイムアウト値と処理時間分布の整合性を確認。
- 改善しなければ、
ffmpeg -c:v copy -an
等で軽量ストリーム化して差を見る。
まとめ
OPENCV_FFMPEG_READ_ATTEMPTS
は、次の映像フレームに辿り着くまで諦めずに読み進めるための気の長さを調整するスイッチです。長尺や多トラック、軽度の破損が混ざる環境では、このスイッチを適切に引き上げるだけで連鎖的な失敗を断ち切れることがあります。最小変更で効きやすい一方、待ち時間は延びうるので、監視・再試行戦略とセットで運用設計するのが現実解です。