FFmpegでVFR(可変フレームレート)をCFR(固定フレームレート)に変換するには、fpsフィルター、または出力側の-fps_mode cfrと-rを使います。どちらも表示時刻に合わせてコマを複製・間引きし、映像を再エンコードします。元ファイルを残し、別名のコピーで比較してください。
VFRであるだけで音ズレするとは限りません。Premiereで後半ほどずれる場合は、先に元動画とタイムラインの音ズレを切り分ける手順を確認します。一定量の音声オフセットや収録時の欠落は、fpsを揃えるだけでは直せません。
変換するか、どのfpsにするかを決める
| 状況 | 確認・処置 |
|---|---|
| 元動画も編集ソフトも正常に同期する | VFRの表示だけを理由に変換しない。納品条件にCFR指定があるかを確認する |
| 元動画は正常だが編集ソフトでずれる | 短い範囲をCFRに変換し、同じ編集設定で比較する |
| 元動画から音がずれている | 入力の時刻・一定の音声オフセット・欠落を先に調べる |
| 30・29.97・60などの指定がある | 撮影設定と編集・納品仕様に合わせる。29.97は30000/1001、59.94は60000/1001を使う |
VFRは、隣り合う映像フレームの表示間隔が変わる形式です。スマートフォンや画面録画のすべてがVFRという意味でも、静止画面だけコマを減らすという定義でもありません。30fpsへの変換で10fps部分のコマを複製しても、その間の新しい動きは復元されません。60fps素材を30fpsにすると動きの情報を間引くため、30を一律の推奨値にはしません。
ffprobeでVFRを調べる
ffprobe -v error -select_streams v:0 -show_entries stream=r_frame_rate,avg_frame_rate,nb_frames,duration,time_base -of default=nw=1 "vfr.mp4"| 項目 | 過去の入力での報告値 | 読み方 |
|---|---|---|
| r_frame_rate | 30/1 | タイムスタンプを表現する基準レートの推定値 |
| avg_frame_rate | 450/23(約19.57fps) | FFmpegが報告する平均レート |
| nb_frames | 120 | 記録されているコマ数。形式によっては取得できない |
| duration | 6.000000 | 映像ストリームの長さ(秒) |
r_frame_rateとavg_frame_rateの差は調査のきっかけで、差がある・ないだけではVFR/CFRを断定できません。報告されたavg_frame_rateは、表示されたnb_frames÷durationと一致するとは限らないため、120÷6から上の報告値を書き換えることもしません。公式AVStream資料でも、r_frame_rateは推定値とされています。
ffprobe -v error -select_streams v:0 -show_entries frame=pts_time -of csv=p=0 "vfr.mp4"
ffmpeg -i "vfr.mp4" -vf vfrdet -an -f null -隣り合うpts_timeの差を比較します。上の過去の入力では0.100秒が30個、約0.033秒が89個でした。分数fpsや時刻の単位による微小な丸め差と、このような明確な間隔の違いを分けて読みます。pts_timeを取得できない入力では、frame=best_effort_timestamp_timeも調査の候補ですが、欠落・破損した時刻を正常とみなすものではありません。
vfrdetは表示時刻の差の変化を検出する補助です。末尾のVFR値を「VFRのコマの割合」と読み替えず、コマごとの時刻と併せて確認してください。
方法1:fpsフィルターで30fpsのコピーを作る
次の例は一般的な8bit SDR動画をH.264・MP4へ変換する設定です。入力はvfr.mp4、出力はcfr.mp4です。-nにより既存の同名出力がある場合は停止します。FFmpegがPATHから実行できるPowerShellの作業フォルダーで使います。
ffmpeg -n -i "vfr.mp4" -map 0:v:0 -map "0:a:0?" -vf fps=30 -c:v libx264 -crf 18 -pix_fmt yuv420p -c:a copy "cfr.mp4"fps=30は一定の表示間隔になるようコマを複製・間引きします。CRF 18はこの例の画質設定で、無劣化ではありません。-mapで最初の映像と、存在すれば最初の音声だけを選ぶため、複数音声・字幕・添付をすべて保存するコマンドではありません。HDR・10bit・透過などを保持する用途には、そのまま流用せず出力の色・ビット深度・形式を別に設計してください。
音声の-c:a copyは再エンコードを省く指定です。MP4に入れられない音声形式ではエラーになるため、その場合は-c:a aac -b:a 192kなどに変更して再エンコードします。音声コピーは元からある音ズレの修正や、編集ソフトでの読み込み成功を保証しません。
2026年9月29日の上の6秒入力では、映像180コマ・6.000秒になりました。10fpsだった前半は同じコマを繰り返して30fpsに揃えています。次の図は表示時刻の説明図で、編集ソフトの操作画面ではありません。

方法2:-fps_mode cfrと-rを使う
ffmpeg -n -i "vfr.mp4" -map 0:v:0 -map "0:a:0?" -fps_mode cfr -r 30 -c:v libx264 -crf 18 -pix_fmt yuv420p -c:a copy "cfr_mode30.mp4"出力側の-rで目標のfpsを指定し、-fps_mode cfrで固定にします。入力より前の-rは入力の時刻の扱いを変える別の指定なので、この例では-iの後に置きます。映像の-c copyと組み合わせて、同じ変換ができるとは考えないでください。
2026年9月29日の既存記録では、この方式の出力が182コマ・6.067秒でした。方法1の180コマとの差は2コマ(約0.067秒)です。一方、以下の10月3日の別入力では両方式とも180コマ・6.000秒になりました。方式だけで末尾の長さを保証せず、入力時刻と出力の終端を確認します。fpsフィルターの公式仕様には時刻の丸めと終端の扱い、FFmpeg公式オプション資料には出力時刻がコンテナ処理で変わる場合も説明されています。
FFmpeg 9.0.2で古いオプションを確認する
このPCのFFmpeg 9.0.2(gyan.dev full build)では、2026年10月3日の再確認でも-vsync cfrが「Unrecognized option ‘vsync’」で終了しました。新しい例は-fps_modeを使います。古い版で非推奨ながら実行できた記録を、今回の版の手順に混ぜません。-fps_mode passthroughと-rで固定化を同時に指定する、過去の矛盾した指定も避けます。
音声つきの6秒サンプルで変換を比較した結果
練習用動画・WAV・スクリプト(ZIP)のvfr-sync.mp4を使いました。自作の模様と合成音による640×360の6秒入力で、前半3秒が10fps、後半3秒が30fps、AAC 48kHz・モノラルの音声を含みます。撮影素材の実際の口の動きを使った音ズレ試験ではありません。
| 入力・出力 | 映像コマ数/長さ | 音声の長さ |
|---|---|---|
| 入力 vfr-sync.mp4 | 120/6.000秒 | 6.000秒 |
| fps=30・音声コピー | 180/6.000秒 | 6.000秒 |
| -fps_mode cfr -r 30・音声コピー | 180/6.000秒 | 6.000秒 |
| HandBrakeCLI –cfr -r 30 | 180/6.000秒 | 6.016秒 |
FFmpegの2出力は約1/30秒間隔で、デコード後の音声のSHA-256は入力と一致し、元ファイルも変わっていませんでした。HandBrakeの出力もデコードできましたが音声は再エンコードされ、終端の報告値は6.016秒でした。これらはファイルとコマ時刻の確認であり、口の動きとの同期やPremiereでの実操作の成功を示す結果ではありません。
練習用VFRを作って変換前後を確かめる
2026年10月2日、Windows 11・ffmpeg 9.0.2 で、次の入力生成と変換を再実行しました。
最初に 基本コマンド記事の練習用動画を作り、sample.mp4 をこの作業フォルダーに置きます。
以下のコマンド例は、動画と出力先を置く作業フォルダーで PowerShell を開いて実行します。
元動画は別名で保管し、出力ファイル名が既存ファイルと重ならないようにしてください。
同名ファイルの上書き確認で止まった場合は N を入力し、別の出力名で実行し直します。
ffmpeg -i sample.mp4 -t 6 -vf "select='if(lt(t,3),not(mod(n,3)),1)'" -fps_mode vfr -c:v libx264 -an vfr.mp4前半3秒は3コマに1コマを残し、後半3秒は全コマを残した、音声のない入力です。
ffmpeg -i vfr.mp4 -vf vfrdet -an -f null -
ffmpeg -i cfr.mp4 -vf vfrdet -an -f null -cfr.mp4 は上の方法1で作成してから確認します。
| 確認項目 | 変換前 vfr.mp4 | 方法1の変換後 cfr.mp4 |
|---|---|---|
| コマ数 | 120 | 180 |
| 長さ | 6.000秒 | 6.000秒 |
| 隣り合うコマの時刻の差 | 0.100秒×30、約0.03333秒×89 | 約0.03333秒×179 |
| vfrdet の末尾ログ | VFR:0.008403 (1/118) | VFR:0.000000 (0/179) |
vfrdet は時刻の差の変化を検出する補助です。
値を「可変のコマの割合」と読み替えず、必要に応じて ffprobe のコマの時刻を併せて確認してください。
小さな差は時刻の丸めによる場合もあるため、今回の0.100秒と0.03333秒のような間隔の違いと区別します。
元動画の音のずれや欠落が、この変換だけで直るとは限りません。
音声つき素材では、変換後を編集ソフトで読み込み、冒頭と末尾の音と映像の同期も確認します。
HandBrakeでCFRに変換する
画面版ではVideo(動画)のFramerateで必要なfpsを選び、Constant Frame Rate(CFR)を選択します。「Same as Source」だけでは固定化を指定したことになりません。操作項目はHandBrake公式のフレームレート資料に基づく説明で、今回の実行はCLI版です。
HandBrakeCLI -i "vfr.mp4" -o "handbrake_cfr30.mp4" --preset "Fast 1080p30" --cfr -r 30HandBrakeCLIは、入力と異なる未使用の出力名で実行します。既存の出力を上書きせずに比較するPowerShellの例は、HandBrakeの使い方・RFとプリセットの選び方に掲載しています。
| 指定 | 2026年9月29日の既存CLI記録 |
|---|---|
| –cfr -r 30 | avg_frame_rate 30/1、180コマ |
| –vfr | avg_frame_rate 800/41(約19.51fps)、120コマ |
変換後に編集へ戻す前の確認
- ffprobeで映像・音声の開始時刻と長さ、映像のコマ間隔を確認する。コンテナ全体のdurationだけで同期成功と判定しない
- 出力を再生し、冒頭・中間・末尾の動き、光や口の動きと音の位置を入力と比べる
- 編集ソフトへ別素材として読み込み、同じタイムライン条件で確認する。必要なら短い書き出しでも比べる
- 元動画を残す。コーデック・色・複数音声・字幕の保持が必要なら選択ストリームと出力仕様を見直す
FFmpegの導入や解像度・CRFなどの基本操作はよく使うFFmpegコマンド、編集ソフトでの切り分けはPremiereの音ズレ対策へ進んでください。関連する操作は動画編集の手順一覧から探せます。
公式資料確認:2026年10月3日。vfrdet、HandBrakeCLIのオプション。2026年9月29日・10月2日の数値は既存の実施記録として保持し、10月3日の新しい検証結果と分けています。
