fqmpeg の C4 クラスタは動画の時間軸を操作する 15 個の動詞だ。切る (trim, split)・繋げる (concat, crossfade)・繰り返す/逆再生する (loop, reverse, boomerang)・速度を変える (speed, interpolate, fps)・端をフェードする (fade, fade-between)・特定のフレームを止める/伸ばす (freeze, repeat-frame, frame-step) — 「素材ができた」から「公開できる」までの間の編集操作を一通りカバーする。
この記事では各動詞が生成する FFmpeg コマンド、デフォルト値、出力ファイル名規則、そして裏で使われているフィルタ由来の落とし穴(atempo の 0.5–2.0 制約、xfade の offset セマンティクス、tpad のタイムスタンプ計算)を 1 つずつ解説する。記述は fqmpeg 3.0.1 の src/commands/ ソースを実読して検証している。
この記事で得られるもの
- 15 動詞をタスク別(カット/結合/時間/トランジション/フレーム)に分類する判断マトリックス
- 各動詞が生成する正確な FFmpeg コマンド(
--dry-run出力で検証済み) - 全動詞のデフォルト値・許容引数・出力ファイル名規則
- スムーズなスローモーション生成パイプラインを含む実用レシピ 3 本
15 動詞の全体像
クラスタは 4 つのタスクグループに綺麗に分かれる。グループから動詞へとたどればよい。
| グループ | 動詞 | 用途 |
|---|---|---|
| カットと結合 | trim, split, concat | 1 区間を切り出す / 等間隔で分割する / 複数ファイルを繋ぐ |
| 時間再生 | loop, reverse, speed, boomerang | N 回再生 / 逆再生 / 早送り遅送り / 順再生→逆再生 |
| トランジション | crossfade, fade, fade-between | 2 クリップをブレンド / 端をフェード / 間に黒を挟む |
| フレーム単位 | freeze, repeat-frame, frame-step, interpolate, fps | 1 フレームを止める / 最終フレームを伸ばす / 間引く / 補間する / フレームレート変更 |
読み始める前に押さえておきたい 3 点:
trimはキーフレーム精度・フレーム精度ではない。-c copyを使うため、カットは指定時刻直前のキーフレームにスナップする。フレーム精度のカットが必要なら再エンコードになる — トレードオフは FFmpeg 無劣化カット記事 で詳しく扱ったspeedは大きな倍率でatempoをチェーンする。FFmpeg のatempoは 1 段あたり 0.5–2.0 しか受け付けないので、4× は内部的にatempo=2.0,atempo=2のチェーンになる。fqmpeg が自動で組んでくれるので倍率は何でも渡してよいinterpolateは CPU 重い。動き補償補間はノートPCで実時間の 5〜20 倍。スムーズな動きが本当に必要なとき(スローモ、フレームレートアップコンバート)だけ使う。単に目標レートに合わせるだけ(タイムコード変換など)ならfpsで十分
カットと結合
trim — 区間を切り出す(ストリームコピー)
--start と --duration または --to の間を切り出す。-c copy なので高速だが、カット位置は直前のキーフレームにスナップする。フレーム精度のカットが必要なら継ぎ目を再エンコードする必要がある。
- ソース:
src/commands/trim.js - コーデック:
-c copy -map 0(パススルー、全ストリーム) - 時刻形式: 秒 (
30) またはHH:MM:SS(00:01:30) --durationまたは--toのいずれかが必須 — どちらも指定しないとError: specify --duration (-d) or --to (-t).で終了
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
-s, --start <time> | 0 | 開始時刻 |
-d, --duration <time> | — | --start からの長さ |
-t, --to <time> | — | 終了時刻(--duration と排他) |
-o, --output <path> | <入力名>-trimmed.<拡張子> | 出力先を上書き |
$ npx fqmpeg trim input.mp4 --start 00:00:10 --duration 00:00:30 --dry-run
ffmpeg -i input.mp4 -ss 00:00:10 -t 00:00:30 -c copy -map 0 input-trimmed.mp4
カット開始位置がキーフレームでない場合、FFmpeg は直前のキーフレームまで遡るため、出力が要求より少し長くなったり最初の数百ミリ秒がフリーズしたりする。これは -c copy の代償。継ぎ目だけ再エンコードする 2 段階アプローチは FFmpeg 無劣化カット記事 で扱っている。
split — 等間隔で分割
FFmpeg の segment muxer を使って N 秒ごとのセグメントに分割する。ストリームコピーなので一瞬で終わる。デフォルト出力パターンは %03d(3 桁ゼロパディング): input-part000.mp4, input-part001.mp4, ...
- ソース:
src/commands/split.js - muxer:
-f segment -segment_time <s> -reset_timestamps 1 - コーデック:
-c copy -map 0
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<seconds> | 必須 | セグメント長(秒) |
-o, --output <pattern> | <入力名>-part%03d.<拡張子> | printf 形式のプレースホルダ付きパターン |
$ npx fqmpeg split input.mp4 60 --dry-run
ffmpeg -i input.mp4 -c copy -map 0 -f segment -segment_time 60 -reset_timestamps 1 input-part%03d.mp4
trim と同じく、セグメント境界はキーフレームにスナップするため、「60 秒」と指定しても実際は 58–62 秒になることがある(GOP 配置次第)。-reset_timestamps 1 で各セグメントの PTS が 0 にリセットされる — これは下流のツール(HLS, DASH, プレイヤ)が期待する動作。
concat — 複数ファイルを繋ぐ
2 つ以上の動画を連結する。2 モードある:
-
demuxer モード(デフォルト、ストリームコピー): 絶対パスで一時
filelist.txtを生成し、-f concat -safe 0で繋ぐ。終了時に一時ファイルは削除。高速だが、入力のコーデック/コンテナ/解像度が一致している必要がある -
再エンコードモード (
--re-encode):filter_complex concatでコーデック・解像度が異なる入力でも連結できる。遅いが寛容 -
デフォルトサフィックス:
-joined
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<inputs...> | 必須 | 2 つ以上の動画ファイル |
--re-encode | off | filter ベースの concat に切り替え(コーデック不一致時に使う) |
-o, --output <path> | <最初の入力名>-joined.<拡張子> | 出力先を上書き |
$ npx fqmpeg concat clip1.mp4 clip2.mp4 clip3.mp4 --dry-run
# File list (auto-generated):
# file '/abs/path/clip1.mp4'
# file '/abs/path/clip2.mp4'
# file '/abs/path/clip3.mp4'
ffmpeg -f concat -safe 0 -i filelist.txt -c copy clip1-joined.mp4
$ npx fqmpeg concat clip1.mp4 clip2.mp4 --re-encode --dry-run
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex [0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa] -map [outv] -map [outa] clip1-joined.mp4
demuxer モードの --dry-run 出力には自動生成されたファイルリストがコメントブロックで含まれる — 絶対パス周りのデバッグに便利。実際の filelist.txt は入力と同じディレクトリにハッシュ付きの名前 .fqmpeg-concat-1715450000000.txt で書かれ、プロセス終了時に unlink される(実行後にファイルシステムには残らない)。
demuxer モードで Non-monotonous DTS in output stream や Could not write header が出たら、入力のタイムスタンプ・コーデック・解像度が異なる可能性が高い。--re-encode を付けて再試行する。
時間再生
loop — N 回再生
-stream_loop を使って N 回ループする。ストリームコピーなので一瞬。
- ソース:
src/commands/loop.js - フィルタ:
-stream_loop <N-1>(FFmpeg の stream_loop は「追加」ループ数を数えるので、loop 3= 3 回再生 =stream_loop 2) - デフォルトサフィックス:
-loop<N>
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<count> | 必須 | 再生回数(3 = 3 回再生) |
-o, --output <path> | <入力名>-loop<N>.<拡張子> | 出力先を上書き |
$ npx fqmpeg loop input.mp4 3 --dry-run
ffmpeg -stream_loop 2 -i input.mp4 -c copy input-loop3.mp4
count は正の数値としてバリデーションされ、整数部分だけが FFmpeg に渡る (Math.floor(n) - 1)。0 や負数を渡すと FFmpeg を呼ぶ前にバリデータエラーになる。
reverse — 逆再生
reverse ビデオフィルタと areverse オーディオフィルタを適用するため、映像も音声も逆再生になる。--no-audio で音声を完全にドロップ可能(areverse が音声ストリーム全体をメモリにバッファするため、長尺ではこちらの方がはるかに速い)。
- ソース:
src/commands/reverse.js - フィルタ:
-vf reverse -af areverse(または--no-audioで-vf reverse -an)
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
--no-audio | (音声保持) | 音声を捨てる(長尺で高速化) |
-o, --output <path> | <入力名>-reversed.<拡張子> | 出力先を上書き |
$ npx fqmpeg reverse input.mp4 --dry-run
ffmpeg -i input.mp4 -vf reverse -af areverse input-reversed.mp4
speed — 再生速度を変える
setpts で映像を、1 つ以上の atempo チェーンで音声を再タイミングする。atempo のチェーン化を fqmpeg が自動でやってくれるので、倍率は何でも渡せる。
- ソース:
src/commands/speed.js - 映像フィルタ:
setpts=(1/speed)*PTS - 音声フィルタ:
atempoチェーン(FFmpeg のatempoは 1 段 0.5–2.0 のみ。4× はatempo=2.0,atempo=2、0.25× はatempo=0.5,atempo=0.5) - デフォルトサフィックス: 倍速時は
<N>x、スロー時はslow<N>x
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<factor> | 必須 | 速度倍率(2 = 2×、0.5 = 半速) |
--no-audio | (音声保持) | 音声を捨てる(タイムラプス時に有用) |
-o, --output <path> | <入力名>-<サフィックス>.<拡張子> | 出力先を上書き |
$ npx fqmpeg speed input.mp4 2 --dry-run
ffmpeg -i input.mp4 -filter:v setpts=0.500000*PTS -filter:a atempo=2 input-2x.mp4
$ npx fqmpeg speed input.mp4 4 --dry-run
ffmpeg -i input.mp4 -filter:v setpts=0.250000*PTS -filter:a atempo=2.0,atempo=2 input-4x.mp4
$ npx fqmpeg speed input.mp4 0.5 --dry-run
ffmpeg -i input.mp4 -filter:v setpts=2.000000*PTS -filter:a atempo=0.5 input-slow0.5x.mp4
タイムラプス用途(高倍速・音声不要)では必ず --no-audio を付ける。atempo を 4 段 5 段とチェーンするのは技術的には動くが、結果は耳に痛いアーティファクトだらけになる。スローモーションが目的なら speed 0.5 は単にフレーム複製になるだけ。中間フレームを合成したいならレシピ 3 のように interpolate をチェーンする。
boomerang — 順再生 + 逆再生
映像ストリームを split して片方を逆再生し、順 + 逆を concat してシームレスなピンポンループを作る。映像は再エンコード、音声はストリームコピー(または --no-audio でドロップ)。
- ソース:
src/commands/boomerang.js - フィルタ:
[0:v]split[fwd][rev];[rev]reverse[reversed];[fwd][reversed]concat=n=2:v=1:a=0
$ npx fqmpeg boomerang input.mp4 --dry-run
ffmpeg -i input.mp4 -filter_complex [0:v]split[fwd][rev];[rev]reverse[reversed];[fwd][reversed]concat=n=2:v=1:a=0 -c:a copy input-boomerang.mp4
音声処理は意図的にシンプル: フィルタグラフは映像のみ concat するため、コピーされた音声 (-c:a copy) は順再生分だけ流れて途中で終わる。Instagram 風クリップなら気にならない(オートプレイで音消し前提のため)。音声も含めてエフェクトの一部にしたいなら --no-audio で意図的な無音にするか、後処理で audio 系動詞でカスタムトラックを作る。
トランジション
crossfade — 2 クリップを xfade でブレンド
FFmpeg の組み込み xfade トランジションを 2 つの動画間に適用する。デフォルトでは ffprobe を clip1 にかけて長さを検出し、トランジションが clip1 の終わり際から始まるよう offset を自動計算する — つまり clip1 が最後まで再生され、そのまま clip2 にスムーズにクロスフェードする。音声側も acrossfade で並行してクロスフェードする。
- ソース:
src/commands/crossfade.js - フィルタ(デフォルト):
[0:v][1:v]xfade=transition=<種類>:duration=<秒>:offset=<auto>[v];[0:a][1:a]acrossfade=d=<秒>[a] - 自動 offset:
offset = ffprobe(clip1).duration - <クロスフェード長>。--offset <n>で上書き可能 - トランジション (21 種):
fade,wipeleft,wiperight,wipeup,wipedown,slideleft,slideright,slideup,slidedown,circlecrop,rectcrop,distance,fadeblack,fadewhite,radial,smoothleft,smoothright,smoothup,smoothdown,squeezev,squeezeh
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input1> | 必須 | 1 本目の動画 |
<input2> | 必須 | 2 本目の動画 |
<duration> | 必須 | クロスフェード長(秒) |
--transition <type> | fade | 上記 21 種のいずれか |
--offset <seconds> | 自動検出 | clip1 タイムライン上のトランジション開始時刻 |
--no-audio-fade | off | acrossfade の代わりに音声をストリームコピー(音声トラックがない場合に使う) |
-o, --output <path> | <input1名>-crossfade.<拡張子> | 出力先を上書き |
# デフォルト: ffprobe で clip1 の長さを自動検出(ここでは clip1 が 8.5 秒)
$ npx fqmpeg crossfade clip1.mp4 clip2.mp4 1.5 --transition wipeleft --dry-run
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex [0:v][1:v]xfade=transition=wipeleft:duration=1.5:offset=7[v];[0:a][1:a]acrossfade=d=1.5[a] -map [v] -map [a] clip1-crossfade.mp4
# 手動 offset、音声クロスフェードなし(片方に音声トラックがないとき)
$ npx fqmpeg crossfade clip1.mp4 clip2.mp4 1.5 --offset 5 --no-audio-fade --dry-run
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex xfade=transition=fade:duration=1.5:offset=5 -c:a copy clip1-crossfade.mp4
自動 offset は最も多い期待挙動 — 「clip1 を最後まで再生してから clip2 へクロスフェード」 — に一致する。重ね合わせを早めに始めたいなら --offset <n> で指定、片方のクリップに音声がないなら --no-audio-fade を使う(acrossfade は音声ストリームが片側に欠けるとエラーになる)。
fade — 端をフェードイン / フェードアウト
開始時のフェードイン、終了時のフェードアウト、または両方を加える。映像 (fade) と音声 (afade) のフィルタが lockstep で発行されるため、音量カーブも視覚フェードと同期する。
- ソース:
src/commands/fade.js --inまたは--outのいずれかが必須--outを指定するときは--duration(動画の総尺)が必須 — フェードアウト開始時刻を計算するため
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
--in <seconds> | 0 | フェードイン長 |
--out <seconds> | 0 | フェードアウト長 |
--duration <seconds> | — | 動画の総尺(--out > 0 のとき必須) |
-o, --output <path> | <入力名>-fade.<拡張子> | 出力先を上書き |
$ npx fqmpeg fade input.mp4 --in 2 --dry-run
ffmpeg -i input.mp4 -vf fade=t=in:st=0:d=2 -af afade=t=in:st=0:d=2 input-fade.mp4
$ npx fqmpeg fade input.mp4 --in 2 --out 2 --duration 30 --dry-run
ffmpeg -i input.mp4 -vf fade=t=in:st=0:d=2,fade=t=out:st=28:d=2 -af afade=t=in:st=0:d=2,afade=t=out:st=28:d=2 input-fade.mp4
総尺は事前に npx fqmpeg duration input.mp4 で取得する。フェードアウト開始時刻は duration - fadeOut で自動計算される。入力に音声トラックがない場合、-af フィルタチェーンが失敗する — 先に strip-audio で音声を取り除くか、--dry-run 出力から -af を削って FFmpeg を直接実行する。
fade-between — 2 クリップの間に黒を挟む
clip1 を黒へフェードアウト、その後 clip2 を黒からフェードインして concat する。crossfade とは違い、間に黒フレームが挟まる(直接ブレンドしない)。音声は単純に end-to-end で連結される(音声側のクロスフェードはなし)。
- ソース:
src/commands/fade-between.js - フィルタ:
[0]fade=t=out:st=0:d=<d>[v0];[1]fade=t=in:st=0:d=<d>[v1];[v0][v1]concat...
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input1> | 必須 | 1 本目の動画 |
<input2> | 必須 | 2 本目の動画 |
--duration <n> | 1 | フェード長(両クリップに適用) |
-o, --output <path> | <input1名>-faded.<拡張子> | 出力先を上書き |
$ npx fqmpeg fade-between clip1.mp4 clip2.mp4 --duration 1.5 --dry-run
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex [0]fade=t=out:st=0:d=1.5[v0];[1]fade=t=in:st=0:d=1.5[v1];[v0][v1]concat=n=2:v=1:a=0[v];[0:a][1:a]concat=n=2:v=0:a=1[a] -map [v] -map [a] clip1-faded.mp4
注意: フェードの st=0(開始時刻 = 0)は各クリップの個別タイムラインに適用される — つまり clip1 は最初からフェードを始める。「clip1 の最後 1.5 秒をフェードアウトして clip2 の最初 1.5 秒をフェードイン」というよくあるパターンには別のフィルタグラフが必要。この動詞は「クリップ自体が短く、全体がランプして良い」ケースに向く。
フレーム単位の操作
freeze — 1 フレームを静止
指定時刻のフレームを指定秒数だけ静止させる。tpad + setpts のトリックを使う。音声はストリームコピー(つまり静止中も音声は流れ続ける)。
- ソース:
src/commands/freeze.js - フィルタ:
tpad=stop_mode=clone:stop_duration=0,setpts='if(gte(T,<at>),if(lte(T,<at>+<hold>),<at>/TB,PTS-<hold>/TB),PTS)'
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<at> | 必須 | 静止する時刻(秒または HH:MM:SS) |
<hold> | 必須 | 静止する長さ(秒) |
-o, --output <path> | <入力名>-freeze.<拡張子> | 出力先を上書き |
$ npx fqmpeg freeze input.mp4 5 2 --dry-run
ffmpeg -i input.mp4 -vf tpad=stop_mode=clone:stop_duration=0,setpts='if(gte(T,5),if(lte(T,5+2),5/TB,PTS-2/TB),PTS)' -c:a copy input-freeze.mp4
setpts 式は <at> 時点で時間を <hold> 秒間止め、その後 <hold> 秒分だけシフトして再開する。音声はストリームコピーなので静止中も流れ続ける(音声側にはパディングが入らない)。完全同期の静止(音声も止める)が必要なら、FFmpeg を直接呼んで -af "asetpts=..." を書くか、trim で 3 区間(前・1 フレーム・後)に分割して中央の 1 フレームに repeat-frame を適用してから concat --re-encode で繋ぐ。
repeat-frame — 最終フレームを伸ばす
最終フレームを N 秒だけ繰り返して動画末尾を延長する。タイトルカードを足したり、短いクリップを目標尺に合わせるのに使う。音声はストリームコピー(音声は元の長さで終わり、伸びた部分は無音)。
- ソース:
src/commands/repeat-frame.js - フィルタ:
tpad=stop_mode=clone:stop_duration=<秒>
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<seconds> | 必須 | 最終フレームを保持する長さ |
-o, --output <path> | <入力名>-hold.<拡張子> | 出力先を上書き |
$ npx fqmpeg repeat-frame input.mp4 3 --dry-run
ffmpeg -i input.mp4 -vf tpad=stop_mode=clone:stop_duration=3 -c:a copy input-hold.mp4
frame-step — N フレームに 1 つだけ残す
N フレームに 1 つだけ残してタイムスタンプを reflow する間引き処理。音声は常に -an で削除される(元のタイミングが意味をなさなくなるため)。出力フレームレートは入力と同じだが、内容は実質 N 倍速のタイムラプス(ただし補間なしで素のまま間引き)。
- ソース:
src/commands/frame-step.js - フィルタ:
select='not(mod(n\,<n>))',setpts=N/FRAME_RATE/TB
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<n> | 必須 | N フレームに 1 つ残す(正の整数) |
-o, --output <path> | <入力名>-step.<拡張子> | 出力先を上書き |
$ npx fqmpeg frame-step input.mp4 5 --dry-run
ffmpeg -i input.mp4 -vf select='not(mod(n\,5))',setpts=N/FRAME_RATE/TB -an input-step.mp4
スムーズなタイムラプスが欲しいなら(カクカクの間引きでなく)speed --no-audio を使う方が良い。speed はタイムスタンプを連続的に縮める一方、frame-step は時間軸方向の最近傍サンプリングに近い。
interpolate — 動き補償でスムーズなスローモーション
FFmpeg の minterpolate を motion-compensated interpolation モードで使い、合成された中間フレームを生成する。これが「スムーズスローモ」動詞 — 30fps 素材を半速にして 60fps へ補間すると、speed 0.5 が出すフレーム複製とは違って滑らかに見える。
- ソース:
src/commands/interpolate.js - フィルタ:
minterpolate=fps=<目標>:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1 - 遅い。動き補償補間は CPU 重め — ノートPCで実時間の 5〜20 倍
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<target-fps> | 必須 | 目標フレームレート(60, 120 など) |
-o, --output <path> | <入力名>-<N>fps-interp.<拡張子> | 出力先を上書き |
$ npx fqmpeg interpolate input.mp4 60 --dry-run
ffmpeg -i input.mp4 -vf minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1 -c:a copy input-60fps-interp.mp4
mi_mode=mci は motion-compensated interpolation(最高品質・最低速)。mc_mode=aobmc は適応的オーバーラップブロックマッチング、me_mode=bidir は双方向動き推定、vsbmc=1 は可変サイズブロック動き補償を有効化。これらは品質寄りのデフォルト。速い動きで shimmering / ghosting が出るときは --dry-run 出力を編集して mi_mode=blend か mi_mode=dup に落として FFmpeg を直接実行する。
fps — フレームレート変更(補間なし)
シンプル版: フレームを drop / duplicate して目標フレームレートに合わせる。動き補償なしなので倍速はカクカク・スローはガタガタ — ただし interpolate が遅いのに対してこちらは一瞬。
- ソース:
src/commands/fps.js - フィルタ:
fps=<rate>
| 引数 / オプション | デフォルト | 備考 |
|---|---|---|
<input> | 必須 | 入力動画 |
<rate> | 必須 | 目標フレームレート(24, 30, 60) |
-o, --output <path> | <入力名>-<N>fps.<拡張子> | 出力先を上書き |
$ npx fqmpeg fps input.mp4 24 --dry-run
ffmpeg -i input.mp4 -vf fps=24 -c:a copy input-24fps.mp4
fps vs interpolate の使い分け: 単に目標レートに合わせたい(60fps ゲームプレイ録画を 30fps チュートリアルに変換するなど)なら fps、本当にスムーズな高フレームレート出力が欲しい(スローモ再生、滑らかなパン)なら interpolate。
実用レシピ
各レシピは複数動詞を組み合わせた実ワークフロー。
レシピ 1: 3 つのクリップをクロスフェードで繋いでアウトロをフェード
よくある編集パターン: 3 本のクリップをスムーズなクロスフェードで連結し、最後に 1 秒のフェードアウトを足す。各 crossfade 呼び出しは前のクリップ長を自動検出してトランジション開始時刻を正しく合わせてくれる。
# Step 1: clip1 と clip2 をクロスフェード(1 秒)
npx fqmpeg crossfade clip1.mp4 clip2.mp4 1 -o c12.mp4
# Step 2: c12 と clip3 をクロスフェード
npx fqmpeg crossfade c12.mp4 clip3.mp4 1 -o c123.mp4
# Step 3: 総尺を取って 1 秒フェードアウト
total=$(npx fqmpeg duration c123.mp4 | awk -F: '{print ($1*3600)+($2*60)+$3}')
npx fqmpeg fade c123.mp4 --out 1 --duration "$total" -o final.mp4
各中間ファイルは再エンコードされる(xfade はフィルタグラフでストリームコピーは効かない)ので、長尺のクリップではエンコードコストが累積する。長尺の場合は Step 2 の --dry-run 出力を編集して 3 本目の入力を直接組み込み、1 つのフィルタグラフで完結させる方が速い。3 本のうち音声がないクリップが 1 つでもあれば --no-audio-fade を付ける — acrossfade は片側に音声がないとエラーで落ちる。
レシピ 2: 30fps 素材からスムーズなスローモ
クリップを半速に落とし、ガクつかないように 60fps へ補間する。2 段パイプラインで、120fps 撮影のクリップを半速再生したのに近い見た目になる。
# Step 1: 0.5x にスロー(音声は捨てる - スロー音声は使い道がほぼない)
npx fqmpeg speed source.mp4 0.5 --no-audio
# → source-slow0.5x.mp4
# Step 2: 60fps へ補間してスムーズに
npx fqmpeg interpolate source-slow0.5x.mp4 60
# → source-slow0.5x-60fps-interp.mp4
Step 2 が遅い — 動き補償補間は実時間の 5〜20 倍。バッチ処理は夜間か高性能マシンで。素材が既に 60fps なら Step 2 は省略可(speed 0.5 で実効 30fps となり、60fps ディスプレイでは十分滑らかに見える)。
レシピ 3: 1 時間の配信からハイライトリール
長い配信から 3 つのハイライトを切り出し、間に黒を挟んで繋ぎ、冒頭に 1 秒のフェードインを足す。
# Step 1: 3 つのハイライトを trim で切り出す
npx fqmpeg trim stream.mp4 --start 00:12:30 --duration 00:00:20 -o h1.mp4
npx fqmpeg trim stream.mp4 --start 00:34:15 --duration 00:00:25 -o h2.mp4
npx fqmpeg trim stream.mp4 --start 00:51:00 --duration 00:00:15 -o h3.mp4
# Step 2: h1 と h2 の間に黒を挟む
npx fqmpeg fade-between h1.mp4 h2.mp4 --duration 0.8 -o h12.mp4
# Step 3: (h1+h2) と h3 の間にも黒を挟む
npx fqmpeg fade-between h12.mp4 h3.mp4 --duration 0.8 -o h123.mp4
# Step 4: 総尺を取って 1 秒フェードイン
total=$(npx fqmpeg duration h123.mp4)
# → 0:00:60.xxxxxx (フェードで両端が少し削れて 60 秒前後)
npx fqmpeg fade h123.mp4 --in 1 --duration 60 -o reel.mp4
各 trim はキーフレームスナップになる — 特定フレームに合わせたカット(アクションの瞬間など)が必要なら、表の冒頭で言及した 無劣化カット記事を参照。Step 2〜4 は再エンコードなので、Step 1 のキーフレームスナップ誤差はパイプライン全体には伝播しない。
よくある質問
loop 3 がなぜ -stream_loop 2 を生成するのか?
FFmpeg の -stream_loop は「追加」ループ数を数える仕様だから。-stream_loop 0 は「1 回再生」、-stream_loop 2 は「1 回再生してさらに 2 回ループ = 計 3 回再生」を意味する。fqmpeg はユーザー意図(「3 回再生」)と一致するようにユーザー指定のカウントから 1 を引いている。
trim は速いけどカット位置がずれる。何が起きている?
trim は -c copy を使うため、出力は指定 --start 直前のキーフレームから始まる。GOP サイズが 250 フレームで 5 秒地点を指定した場合、実際は 4.0 秒くらいから始まることがある。トレードオフは速度 — ストリームコピーは I/O バウンド(SSD なら一瞬)だが、フレーム精度のカットは継ぎ目の GOP を再エンコードする必要がある。継ぎ目だけ再エンコードする方法は FFmpeg 無劣化カット記事で扱った。
concat が "Non-monotonous DTS" で落ちる。どうすればいい?
入力のタイムスタンプ・コーデック・解像度が異なっており、ストリームコピー concat ではマージできない。--re-encode を付けて再実行する — filter_complex concat に切り替わり、全部デコード→再エンコードするため遅いが不一致を許容する。多数のクリップのうち 1 つだけ異質ならば、まずその 1 つを compress で揃えてから demuxer concat する手もある。
speed は高倍速で atempo をチェーンする。音質に影響は?
聞いて分かるレベルで悪化する。各 atempo インスタンスはフェーズボコーダーによる時間伸縮を適用するため、2〜3 段なら一般用途で許容範囲だが、音楽のような音程要素では warbling(揺れ)が目立つ。タイムラプス用途(高倍速)なら --no-audio を付けて、後から音楽を載せる方が良い。中程度の倍速(1.0〜2.0×)なら atempo は 1 段で済むので音質は問題なし。
frame-step は speed と同じ結果になる?
ならない。frame-step N は N フレームに 1 つだけ残してタイムスタンプを reflow する「ハード間引き」で補間なし。speed N はタイムスタンプを連続的に rescale するため、ソースのフレームを全部保持したまま再生が速くなる。タイムラプス: スムーズな動きが欲しいなら speed --no-audio、カクカクの間引きルックが欲しいなら frame-step。
freeze で音声も止められる?
そのままではできない(音声はストリームコピーされ、静止中も流れ続ける)。同期した静止が必要なら、trim で 3 区間(静止前・静止する 1 フレーム・静止後)に分割し、中央の 1 フレームに repeat-frame で必要長を適用してから concat --re-encode で繋ぐパターンが綺麗。
interpolate は遅い。どんなときに使う価値がある?
最終的な見た目に「スムーズな動き」が必要なときだけ: 速いアクションのスローモ再生、ハイリフレッシュレートディスプレイへ送る前のアニメ素材、24fps 素材を 60fps プラットフォーム向けに救済するなど。チュートリアル画面録画を再エンコードするだけ(速い動きがない)には不要、タイムラプス出力には逆効果(補間方向が間違う)、再エンコードがかかるプラットフォーム(YouTube, Instagram)への投稿でも効果が薄い(再エンコードでスムーズさが大半失われる)。
フォルダ内の動画を一括で固定長にトリムするには?
普通のシェルループ:
for v in raw/*.mp4; do
npx fqmpeg trim "$v" --start 0 --duration 60 -o "trimmed/$(basename "$v")"
done
各出力は trimmed/ に同名で出る。フレーム精度のバッチカットが必要なら、trim を 無劣化カット記事 の再エンコードパターンに置き換える。
まとめ
C4 の 15 動詞は、典型的な編集パスで触る時間軸操作を一通りカバーする:
trim,split,concatはカットと結合(コーデック一致時はストリームコピー、不一致時は--re-encode)loop,reverse,speed,boomerangは時間再生(speedのatempoチェーン化、reverseの音声バッファコストに注意)crossfade,fade,fade-betweenはトランジション(crossfadeは clip1 長を自動検出して音声も並行クロスフェード。--offsetや--no-audio-fadeで挙動を上書きできる)freeze,repeat-frame,frame-step,interpolate,fpsはフレーム単位(interpolateは本当にスムーズさが必要なときだけ — 遅い)
すべての動詞は --dry-run で内部の FFmpeg コマンドを表示するため、デフォルトが用途に合わないとき(freeze の音声処理、カスタム crossfade --offset など)はコマンドをコピーしてカスタマイズし、FFmpeg を直接実行できる。フレーム精度のトリミングや fqmpeg 全体マップは 無劣化カット記事と fqmpeg complete guide を参照。