32blogby Studio Mitsu

fqmpeg オーバーレイ: 13動詞で合成と装飾

fqmpegの13動詞 — ウォーターマーク・テキスト・PIP・グリッド・解析オーバーレイ。実装読みで分かったデフォルトと落とし穴も全部書いた。

by omitsu32 min read
目次

fqmpeg の C8 クラスタは「動画の上に何かを乗せる、もしくは横に並べる」13動詞の合成ツールボックスだ。静的なグラフィックやテキストを描く5つ(watermark, text, drawbox, timecode, border)、複数動画を1枚のキャンバスに合成する4つ(pip, pip-grid, blend, stack)、静止画と音声から動画を作る1つ(picture)、そしてレビュー用に解析情報をフレームに焼き付ける3つ(video-info-overlay, histogram-overlay, progress)。

この記事は fqmpeg 3.0.3src/commands/ を実読して、各動詞が叩いている FFmpeg フィルタ・デフォルト値・出力ファイル名・そして --help だけだと見えない落とし穴を全部洗い出したものだ(text:' を自動エスケープしてくれるのでそのまま書ける、pip-grid は入力本数からレイアウト(2x1/2x2/3x3)を自動判定する、progress はフィルタ式の中から動画の総尺を参照できないので duration を引数で渡す必要がある、histogram-overlay の表示位置とサイズはハードコード)。

この記事で得られるもの

  • 13動詞をタスク別(グラフィック/合成/静止画→動画/解析)に選び分ける早見表
  • 各動詞が実際に生成する FFmpeg コマンド(--dry-run で検証済み)
  • 全コマンドのデフォルト値・範囲・位置キーワード・出力ファイル名
  • 「YouTube 用にブランディング」「比較リール」「画面録画ドキュメント化」の実用レシピ3本

13動詞の全体像

クラスタはタスク別に4グループに分かれる。グループを決めてから動詞を選ぶ流れがラク。

グループ動詞用途
静的グラフィック・テキストwatermark, text, drawbox, timecode, borderロゴ画像・テキスト・矩形・タイムコード・装飾フレームを焼き付ける
複数動画の合成pip, pip-grid, blend, stackピクチャ・イン・ピクチャ、2-9入力グリッド、不透明度ブレンド、左右/上下並べ
静止画→動画picture静止画+音声から再生可能な MP4 を作る(ポッドキャスト・楽曲動画)
解析オーバーレイvideo-info-overlay, histogram-overlay, progressフレーム番号/PTS/ピクチャタイプ、ヒストグラム/波形、時間連動プログレスバー

読み進める前に知っておくと得な3点:

  1. text:' を自動エスケープしてくれる。 drawtext フィルタは : をオプション区切り、' を文字列デリミタとして扱うので、本来 Time: 12:34 のような文字列は手で \: にエスケープが必要。fqmpeg 側で ''\\\\'':\: に変換してから埋め込んでくれるので、シェルクオートだけ気にすればそのまま書ける。逆に「文字列の中から drawtext のオプションを差し込む」ような攻撃的な使い方はブロックされる仕様。
  2. pip-grid は入力本数からレイアウトを自動判定する。 2本なら 2x1、3-4本で 2x2、5-9本で 3x3。グリッドの空きスロットは「最後の入力」で埋められる — 5本を 3x3 に渡すと 6-9 番目のスロットは入力5のクローン。意図的に形を固定したい時は --layout 2x2 で明示する。
  3. progress<duration> を位置引数で渡す必要がある。 FFmpeg の drawbox 式は t(現在再生時刻)は参照できるが、ソースの総尺を表す式が無い。fqmpeg はワークアラウンドとして duration を引数で取り、iw*t/<duration> の式に焼き込む。総尺が不明なら先に npx fqmpeg duration input.mp4 で測ってから渡す。

静的グラフィック・テキスト

watermark — 画像オーバーレイ(PNG ロゴなど)

動画の上に画像を9つのプリセット位置のいずれかで重ねる。音声はコピーで触らない。

  • ソース: src/commands/watermark.js
  • フィルタ: overlay=<x>:<y><x>:<y>--pos--margin から計算)
  • 音声: -c:a copy(無加工)
引数 / オプションデフォルト選択肢・備考
<input>必須入力動画
<image>必須ウォーターマーク画像(透過 PNG 推奨)
--pos <position>bottom-righttop-left, top, top-right, left, center, right, bottom-left, bottom, bottom-right
--margin <n>10端からのマージン(ピクセル)
-o, --output <path><入力名>-watermarked.<拡張子>
bash
$ npx fqmpeg watermark input.mp4 logo.png --pos bottom-right --margin 10 --dry-run

  ffmpeg -i input.mp4 -i logo.png -filter_complex overlay=W-w-10:H-h-10 -c:a copy input-watermarked.mp4

画像の元サイズがそのまま使われる — --scale は無い。ロゴが大きすぎる時は画像エディタか、事前に ffmpeg -i logo.png -vf scale=120:-1 logo-small.png でリサイズしておく。中央寄せ系の位置(top, bottom, center, left, right)は中心軸からの計算なので、--margin は片方の軸にしか効かない(例: bottom は下端からのマージンだけが効き、横方向は中央固定)。

text — タイトル・テロップを焼き付ける

文字列を7つのプリセット位置に焼き付ける。背景ボックスや表示時間ウィンドウもオプション。

  • ソース: src/commands/text.js
  • フィルタ: drawtext=text='<エスケープ済み>':fontsize=N:fontcolor=C:<pos>[:box=1:boxcolor=...][:enable='...']
  • 自動エスケープ: 入力文字列の ': は埋め込み前に変換される
引数 / オプションデフォルト備考
<input>必須入力動画
<string>必須描画する文字列(位置引数。シェルの特殊文字は自分でクオート)
--pos <position>centertop-left, top, top-right, center, bottom-left, bottom, bottom-right
--font-size <n>48正の整数
--color <name>whiteFFmpeg のカラー名 or 0xRRGGBB
--bg <color>(なし)背景ボックス色(例: black@0.5 で 50% 不透明)
--start <sec>(なし)この時刻から表示
--end <sec>(なし)この時刻で非表示
-o, --output <path><入力名>-text.<拡張子>
bash
$ npx fqmpeg text input.mp4 "Hello, World" --pos top --font-size 64 --color yellow --bg black@0.5 --dry-run

  ffmpeg -i input.mp4 -vf "drawtext=text='Hello, World':fontsize=64:fontcolor=yellow:x=(w-text_w)/2:y=20:box=1:boxcolor=black@0.5:boxborderw=8" -c:a copy input-text.mp4

コロンとアポストロフィを含む文字列もそのまま渡せる — --bg "black@0.5"text "Time: 12:34" も手動エスケープは不要。boxborderw=8(ボックス内側の余白8ピクセル)はハードコードなので、もっと余白を減らしたい/増やしたい時は --dry-run をコピーして編集する。複数行は drawtext 自体に折返し機能が無いので、文字列の中に \n を入れて改行を明示する(多くのビルドで動くが行間調整は line_spacing=N を手で足す必要があるかも)。

--start--enddrawtextenable 式で時間ウィンドウを作る。片方だけなら gte(t,N)lte(t,N)、両方なら between(t,A,B) が生成される。

drawbox — 矩形を描画する

固定位置に矩形(枠線 or 塗りつぶし)を描く。チュートリアルで「ここに注目」を強調する、固定領域をマスクする、画面録画にマークアップする、といった用途。

  • ソース: src/commands/drawbox.js
  • フィルタ: drawbox=x=X:y=Y:w=W:h=H:color=C:t=T[:enable='...']
  • 領域フォーマット: x:y:w:h の厳格 regex — 違うフォーマットは Error: region must be x:y:w:h で終了
引数 / オプションデフォルト備考
<input>必須入力動画
<region>必須x:y:w:h(左上原点のピクセル座標)
--color <name>redFFmpeg カラー名
--thickness <n>3正の整数 or 文字列 fill(塗りつぶし)
--start <sec>(なし)この時刻から表示
--end <sec>(なし)この時刻で非表示
-o, --output <path><入力名>-boxed.<拡張子>
bash
$ npx fqmpeg drawbox input.mp4 100:50:200:150 --color yellow --thickness fill --dry-run

  ffmpeg -i input.mp4 -vf drawbox=x=100:y=50:w=200:h=150:color=yellow:t=fill -c:a copy input-boxed.mp4

--thickness fill は「個人情報マスク」のパターン — 単色で領域を覆い隠す。枠線だけのハイライト用途なら正の整数(1080p なら 38 が読みやすい)。--start/--end と組み合わせれば、チュートリアルで「ここを説明する数秒だけ枠を出す」が一発で書ける。座標は絶対ピクセル指定なので、相対指定したい時は npx fqmpeg info input.mp4 で先にフレームサイズを調べる。

timecode — 再生時刻を焼き付ける

動画の現在再生位置を HH:MM:SS.mmm 形式で、半透明の黒ボックス付きで角に表示する。

  • ソース: src/commands/timecode.js
  • フィルタ: drawtext=text='%{pts\\:hms}':fontsize=N:fontcolor=C:<pos>:box=1:boxcolor=black@0.5:boxborderw=4
  • フォーマット: %{pts:hms} — FFmpeg ビルトインの HMS フォーマッタ(ハードコードなので他形式は不可)
引数 / オプションデフォルト備考
<input>必須入力動画
--pos <position>top-lefttop-left, top-right, bottom-left, bottom-right(角のみ)
--font-size <n>24正の整数
--color <name>whiteFFmpeg カラー名
-o, --output <path><入力名>-timecode.<拡張子>
bash
$ npx fqmpeg timecode input.mp4 --pos top-right --font-size 32 --dry-run

  ffmpeg -i input.mp4 -vf drawtext=text='%{pts\:hms}':fontsize=32:fontcolor=white:x=w-text_w-10:y=10:box=1:boxcolor=black@0.5:boxborderw=4 -c:a copy input-timecode.mp4

レビュー用リールに便利 — クライアントが「0:01:22.500 のカットが硬い」と時刻指定でフィードバックしてくるので、確認の往復が一発で終わる。黒ボックスは明るいシーンでも視認できるようにハードコード。違うフォーマット(フレーム番号、ソースタイムコード、カスタム接頭辞)が欲しい時は、text%{pts} / %{n} / %{localtime} を直接書くか、後述の video-info-overlay を使う。

border — 装飾フレームを足す

四方を単色でパッドする。出力サイズが片軸あたり 2 * width ピクセル増える。

引数 / オプションデフォルト備考
<input>必須入力動画
--width <px>20各辺に追加されるボーダー厚(ピクセル)
--color <name>whiteFFmpeg カラー名
-o, --output <path><入力名>-bordered.<拡張子>
bash
$ npx fqmpeg border input.mp4 --width 30 --color black --dry-run

  ffmpeg -i input.mp4 -vf pad=iw+30*2:ih+30*2:30:30:color=black -c:a copy input-bordered.mp4

出力サイズが大きくなる — 1920x1080 のソースに --width 301980x1140 になる。アスペクト比を厳しく見るプラットフォーム(Instagram の 1:1 ポスト、YouTube Shorts の 9:16)は再クロップしてくるので、ターゲットの仕様を先に確認する。縦動画を正方形フレームにしたい時は、--width を「ソース幅と狙う正方形辺の差の半分」に設定して、crop と組み合わせるほうが制御しやすい。

複数動画の合成

pip — ピクチャ・イン・ピクチャ

「サブ」動画をスケールダウンして「メイン」動画の四隅のいずれかに重ねる。

  • ソース: src/commands/pip.js
  • フィルタ: [1]scale=iw*S:ih*S[pip];[0][pip]overlay=<xy>
  • 音声: 明示指定なし — メイン動画(最初の入力)のオーディオが FFmpeg のデフォルト挙動でそのまま通る
引数 / オプションデフォルト範囲備考
<main>必須背景動画
<small>必須オーバーレイ動画
--pos <position>bottom-righttop-left, top-right, bottom-left, bottom-right
--scale <n>0.250.11.0メインに対するオーバーレイの縮小率
--margin <n>10正の整数端からのピクセル距離
-o, --output <path><main名>-pip.<拡張子>
bash
$ npx fqmpeg pip main.mp4 webcam.mp4 --pos top-right --scale 0.3 --dry-run

  ffmpeg -i main.mp4 -i webcam.mp4 -filter_complex [1]scale=iw*0.3:ih*0.3[pip];[0][pip]overlay=main_w-overlay_w-10:10 main-pip.mp4

定番の使い方は「画面録画 = メイン」「ウェブカム = サブ」を25%スケールで角に置く構成。出力の長さはメイン動画準拠なので、サブが短ければ最終フレームでフリーズし、長ければカットされる。サブの最終フレームで延長したい時は tpad=stop_mode=clone を事前にかけるか、サブに対して先に repeat-frame 動詞をかける。

pip-grid — マルチ入力グリッド合成

2-9本の動画をグリッド(2x1, 1x2, 2x2, 3x3)に並べる。--layout 省略時は入力本数から自動判定。

  • ソース: src/commands/pip-grid.js
  • フィルタ: 各入力を scale=iw/cols:ih/rows、各行 hstack、最後に vstack
  • 空きスロット: 最後の入力で埋める
  • 音声: 入力0からコピー(-c:a copy
引数 / オプションデフォルト備考
<inputs...>必須2-9本の入力動画(位置引数、可変長)
--layout <grid>自動2x1, 1x2, 2x2, 3x3。自動: 2→2x1, 3-4→2x2, 5-9→3x3
-o, --output <path><入力0名>-grid<layout>.<拡張子>
bash
$ npx fqmpeg pip-grid clip1.mp4 clip2.mp4 clip3.mp4 clip4.mp4 --layout 2x2 --dry-run

  ffmpeg -i clip1.mp4 -i clip2.mp4 -i clip3.mp4 -i clip4.mp4 \
    -filter_complex '[0:v]scale=iw/2:ih/2[v0];
                     [1:v]scale=iw/2:ih/2[v1];
                     [2:v]scale=iw/2:ih/2[v2];
                     [3:v]scale=iw/2:ih/2[v3];
                     [v0][v1]hstack=inputs=2[row0];
                     [v2][v3]hstack=inputs=2[row1];
                     [row0][row1]vstack=inputs=2[out]' \
    -map '[out]' -c:a copy clip1-grid2x2.mp4

5本を 3x3 に渡すとスロット6-9は入力5のクローンになる。「同じ台詞のテイク5つを9-up でレビュー」みたいな用途なら便利だが、本数の渡し間違いだと意図しない複製になるので注意。3x3 で意図的に空きスロットを残したい時は、黒画面動画を別途作って入力に混ぜる(fqmpeg は空のスロットを合成してくれない)。各入力は iw/cols:ih/rows で機械的にスケールされるので、アスペクト比の違う動画を混ぜると不均一に伸縮される — 揃えたければ事前に resizecrop を通す。

blend — 不透明度ブレンド

2本の動画を、2本目を任意の透明度で重ねる形で合成する。

  • ソース: src/commands/blend.js
  • フィルタ: [1]format=yuva420p,colorchannelmixer=aa=O[over];[0][over]overlay
  • 音声: 再エンコード(-c:a copy 指定なし)
引数 / オプションデフォルト範囲備考
<input1>必須ベース(下)動画
<input2>必須オーバーレイ(上)動画
--opacity <n>0.501入力2のアルファ
-o, --output <path><入力1名>-blend.<拡張子>
bash
$ npx fqmpeg blend input1.mp4 input2.mp4 --opacity 0.5 --dry-run

  ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex [1]format=yuva420p,colorchannelmixer=aa=0.5[over];[0][over]overlay input1-blend.mp4

2本の動画は空間的に合っている必要がある — blend0:0 で重ねるので解像度が違うとずれる。事前にオーバーレイ側を resize でベースに合わせる。出力長は FFmpeg overlay のデフォルトに従う(短いほうに合わせるのが一般的)。「残像エフェクト」狙いなら --opacity 0.30.40.7 を超えるとベース動画がほぼ見えなくなる。

stack — 左右並べ・上下並べ

2本の動画を1フレームに並べる。横並び(horizontal)は高さ一致、縦並び(vertical)は幅一致が前提。

  • ソース: src/commands/stack.js
  • フィルタ: [0:v][1:v]hstack=inputs=2 または vstack=inputs=2
  • 音声: -c:a copy(入力0のトラック)
引数 / オプションデフォルト選択肢備考
<input1>必須左/上の動画
<input2>必須右/下の動画
--direction <dir>horizontalhorizontal, vertical
-o, --output <path><入力1名>-stacked.<拡張子>
bash
$ npx fqmpeg stack original.mp4 graded.mp4 --direction horizontal --dry-run

  ffmpeg -i original.mp4 -i graded.mp4 -filter_complex [0:v][1:v]hstack=inputs=2 -c:a copy original-stacked.mp4

定番の Before / After 比較リール — カラーグレーディング前後、スタビ前後、デノイズ前後など。高さが揃ってないと Input 1 height 720 does not match input 0 height 1080 で落ちるので、npx fqmpeg resize input.mp4 --height 1080 で先に揃える。音声は input1 のものだけ使われ、2本目の音声は捨てられる。

静止画→動画

picture — 静止画 + 音声 → 動画

静止画を音声の長さだけループしてエンコードする。ポッドキャスト1話分や1曲を YouTube 投稿用の動画にする時の定番。

  • ソース: src/commands/picture.js
  • コーデック: libx264 + aac@192k + yuv420p + -tune stillimage + -shortest
  • 出力名: 音声ファイル名から派生(画像名ではない)。<音声名>-video.mp4
引数 / オプションデフォルト備考
<image>必須JPG / PNG カバー画像
<audio>必須MP3 / WAV など
-o, --output <path><音声ディレクトリ>/<音声名>-video.mp4派生元は音声側
bash
$ npx fqmpeg picture cover.png episode.mp3 --dry-run

  ffmpeg -loop 1 -i cover.png -i episode.mp3 -c:v libx264 -tune stillimage -c:a aac -b:a 192k -pix_fmt yuv420p -shortest episode-video.mp4

-tune stillimage は x264 に「フレームの中身が動かない」ヒントを渡すチューニング — 1枚しか実質的な絵が無いので、極端に低いビットレートでも高画質を維持できる。-shortest は短いほうの入力に合わせて出力を終わらせる指定 — ループ画像は無限長なので、必ず音声長で終わる。出力名が音声ベースなのは「このエピソード/曲を動画化する」というユースケースに合わせた設計。違う名前にしたいなら -o で上書きする。

カバー画像つきポッドキャストでチャプターマーカーも入れたい場合は、picture の後に embed-thumbnail やメタデータ系動詞を別途繋ぐ — fqmpeg は単機能で切り分ける設計。

解析オーバーレイ

この3動詞はフレームに診断データを焼き付ける — QC レビュー、カラコレ作業、フレーム単位で何が起きているかを見せたいチュートリアル動画などで使う。最終納品用ではない。

video-info-overlay — フレーム番号・PTS・ピクチャタイプ

各フレームに「フレーム番号 (%{n})」「再生タイムスタンプ (%{pts:hms})」「ピクチャタイプ (%{pict_type} — H.264 の I/P/B)」をテキストで焼き付ける。

  • ソース: src/commands/video-info-overlay.js
  • フィルタ: drawtext=text='frame %{n} | pts %{pts\\:hms} | type %{pict_type}':x=10:y=10:fontsize=16:fontcolor=white:box=1:boxcolor=black@0.5
  • 位置・フォント・色: 全部ハードコード(オプション無し)
引数 / オプションデフォルト備考
<input>必須入力動画
-o, --output <path><入力名>-info-overlay.<拡張子>
bash
$ npx fqmpeg video-info-overlay input.mp4 --dry-run

  ffmpeg -i input.mp4 -vf "drawtext=text='frame %{n} | pts %{pts\:hms} | type %{pict_type}':x=10:y=10:fontsize=16:fontcolor=white:box=1:boxcolor=black@0.5" -c:a copy input-info-overlay.mp4

「フレーム4523 の P フレームでグリッチが出てる」みたいな QC や、GOP 構造を視覚化するチュートリアルに便利。位置は左上、フォントサイズは16px固定 — 1080p で読める最小限。違うサイズや位置が欲しい時は --dry-run をコピーして編集するか、text 動詞で同じ %{...} 式を自分で組む。

histogram-overlay — RGB / 波形 / パレード

320x240 のヒストグラム(または波形 / パレード)を右下隅に重ねる。カラコレ作業でフレームと一緒にレベル分布を見たい時用。

  • ソース: src/commands/histogram-overlay.js
  • フィルタ (levels): split[main][hist];[hist]histogram,scale=320:240[h];[main][h]overlay=W-w-10:H-h-10
  • フィルタ (waveform): split[main][wave];[wave]waveform=mode=column,scale=320:240[w];[main][w]overlay=W-w-10:H-h-10
  • フィルタ (parade): split[main][par];[par]waveform=mode=column:display=parade,scale=320:240[p];[main][p]overlay=W-w-10:H-h-10
  • 位置・サイズ: 右下マージン10px、320x240 パネル — どちらもハードコード
引数 / オプションデフォルト選択肢備考
<input>必須入力動画
--mode <mode>levelslevels, waveform, parade
-o, --output <path><入力名>-histogram.<拡張子>
bash
$ npx fqmpeg histogram-overlay input.mp4 --mode parade --dry-run

  ffmpeg -i input.mp4 -filter_complex split[main][par];[par]waveform=mode=column:display=parade,scale=320:240[p];[main][p]overlay=W-w-10:H-h-10 -c:a copy input-histogram.mp4

levels は古典的な256ビン RGB ヒストグラム。waveform は輝度を横位置に対する縦方向の散布で見せる放送用スコープ。paradewaveform を R/G/B チャンネルごとに3列並べたもの。DIY カラコレで分布の動きをスクラブしながら見たい時は parade が一番情報量が多い。

progress — 時間連動プログレスバー

フレームの上端 or 下端に、時間とともに左から伸びる横棒を焼き付ける。

  • ソース: src/commands/progress.js
  • フィルタ: drawbox=x=0:y=<y>:w='iw*t/<duration>':h=H:color=C:t=fill
  • なぜ duration が必要なのか: FFmpeg の drawbox 式は t(現在再生時刻)は参照できるが、ソースの総尺を表す変数が無い。fqmpeg は duration を引数で受けて式に焼き込むことで回避している
引数 / オプションデフォルト備考
<input>必須入力動画
<duration>必須動画の総秒数(npx fqmpeg duration input.mp4 で測れる)
--pos <position>bottomtop, bottom
--color <name>redFFmpeg カラー名
--height <px>5正の整数
-o, --output <path><入力名>-progress.<拡張子>
bash
$ npx fqmpeg progress input.mp4 90 --pos bottom --color cyan --height 8 --dry-run

  ffmpeg -i input.mp4 -vf drawbox=x=0:y=ih-8:w='iw*t/90':h=8:color=cyan:t=fill -c:a copy input-progress.mp4

duration は秒数で渡す — 1分30秒なら 90、1時間なら 3600。バーは t = duration で画面いっぱいに到達する。間違った値を渡すと早く満タンになるか、最後まで埋まらないかのどちらか。実用シェルワンライナーは dur=$(npx fqmpeg duration input.mp4) && npx fqmpeg progress input.mp4 $dur。視聴者が残り時間を視覚で把握できるチュートリアル動画にどうぞ。

実用ユースケース

各レシピは複数動詞を実際の作業フローに繋ぐ例。

レシピ1: YouTube 用にブランディング

完成済み動画にコーナーロゴ・冒頭3秒のタイトル・薄いプログレスバーを追加する。

bash
# Step 1: コーナーウォーターマーク(透過 PNG ロゴ)
npx fqmpeg watermark final.mp4 logo.png --pos bottom-right --margin 20
# → final-watermarked.mp4

# Step 2: 冒頭3秒だけタイトル表示
npx fqmpeg text final-watermarked.mp4 "Episode 12 — Color Grading" \
  --pos top --font-size 56 --color white --bg "black@0.5" \
  --start 0 --end 3
# → final-watermarked-text.mp4

# Step 3: 薄いプログレスバー(8分エピソード = 480秒)
npx fqmpeg progress final-watermarked-text.mp4 480 --pos bottom --color "white@0.6" --height 4
# → final-watermarked-text-progress.mp4

3パスなので H.264 の世代再エンコードが3回入る。許容範囲だが世代劣化はゼロではない。1パスにしたい時は3つの --dry-run 出力からフィルタ文字列をコピーして ; , で繋いだ FFmpeg 1コマンドにまとめる。週次ポッドキャストみたいに繰り返し回す用途なら、3ステップワークフローのほうが楽でいい — 微小な画質劣化より便利さが勝つ。

レシピ2: タイムコード付き比較リール

カラーグレーディング前後を左右並べて、レビュアーが時刻を指定できるようタイムコードも焼く。

bash
# Step 1: 横並び(高さ一致が前提)
npx fqmpeg stack original.mp4 graded.mp4 --direction horizontal
# → original-stacked.mp4 (1920x1080 が2本なら 3840x1080)

# Step 2: タイムコードを左上に焼き付ける
npx fqmpeg timecode original-stacked.mp4 --pos top-left --font-size 32
# → original-stacked-timecode.mp4

# Step 3 (任意): 各サイドに静的ラベル
npx fqmpeg text original-stacked-timecode.mp4 "ORIGINAL          GRADED" \
  --pos bottom --font-size 48 --color white --bg "black@0.5"

Step 3 のラベルはスペースで「GRADED」を右寄せに押す乱暴な方法だが意外と通用する。ピクセル精度のラベルが欲しいなら、text を2回呼んで --dry-run をコピーし、--pos 由来の x= を絶対座標に書き換える。

レシピ3: 画面録画にウェブカム + プログレス

12分の画面録画(screen.mp4)とウェブカム(webcam.mp4)から、ウェブカム角配置・プログレスバー・冒頭5秒のタイトル付きチュートリアル動画を作る。

bash
# Step 1: ウェブカムを PIP に
npx fqmpeg pip screen.mp4 webcam.mp4 --pos bottom-right --scale 0.2 --margin 20
# → screen-pip.mp4

# Step 2: プログレスバー(12分 = 720秒)
npx fqmpeg progress screen-pip.mp4 720 --pos top --color "red@0.7" --height 6
# → screen-pip-progress.mp4

# Step 3: 冒頭タイトル
npx fqmpeg text screen-pip-progress.mp4 "Vercel + Next.js のセットアップ" \
  --pos center --font-size 64 --color white --bg "black@0.7" \
  --start 0 --end 5
# → screen-pip-progress-text.mp4

ウェブカムが画面録画と違うアスペクト比(正方形ウェブカム vs 16:9 画面)の場合、pip のスケールはメイン側の寸法基準になる — 1920幅の画面に 0.2 指定なら、ウェブカム本体のサイズに関係なく384幅のオーバーレイになる。ウェブカムのアスペクトを保ちたい時は、事前に npx fqmpeg crop webcam.mp4 ... で整形してから pip に通す。

よくある質問

text の文字列にコロンやアポストロフィを入れたいときは?

そのまま入れて OK — npx fqmpeg text input.mp4 "Time: it's 12:34" で動く。fqmpeg のラッパーが :'drawtext に埋め込む前にエスケープしてくれる(''\\\\'':\:)。シェル特殊文字(クオートやバッククオート)はシェル側のクオートで吸収するだけで他は何もいらない。

pip-grid に5本渡したら下の段が複製されたんだけど仕様?

仕様。入力本数がグリッドを埋めない時、空きスロットは「最後の入力」で埋められる。5本を 3x3(自動判定)に渡すと、スロット6-9 は入力5のクローン。4本で 2x2 を埋めたい(パディングをスキップしたい)なら、入力4本のまま渡す(自動で 2x2 が選ばれる)か --layout 2x2 で明示する。意図的に空白スロットを残したいなら、自分で黒画面動画を作って入力に混ぜる — fqmpeg は空きを合成してくれない。

progress に duration を毎回手で渡すのが面倒なんだけど?

FFmpeg の drawbox フィルタ式には「現在時刻 t」は使えるが「ソース総尺」を表す式が無い。fqmpeg はワークアラウンドとして duration を引数で受け取って式に焼き込んでいる。透過的に解決するなら ffprobe をラッパーから呼ぶ実装も可能だが、fqmpeg は単機能動詞の設計を保つために自動 probe はしない。実運用では dur=$(npx fqmpeg duration input.mp4) で取得して $dur を渡すワンライナーで十分。

watermarkpicture の違いは?

watermark は既存の動画の上に静止画を重ねる — 入力は動画、ウォーターマークは小さい画像。picture はその逆で、静止画と音声トラックから動画を作る(音声の長さだけ画像をループ)。内部的には libavfilter の同じ仕組みを使っているが、解こうとしている問題が違う:ブランディング (watermark) vs 1枚画像動画化 (picture)。

histogram-overlay の位置や表示サイズは変えられる?

fqmpeg のフラグからは変えられない — どちらもハードコード(overlay=W-w-10:H-h-10 で右下マージン10px、size=320x240)。カスタムレイアウトが欲しいなら、--dry-run のフィルタ文字列をコピーして overlay=...size=... を書き換えてから FFmpeg を直接叩く。よくある書き換え: 左下にしたければ overlay=10:H-h-10、大きいパネルなら size=480x270

stack で「Input 1 height ... does not match」が出た

hstack は全入力の高さ一致が必須、vstack は幅一致が必須。違うほうを事前にリサイズする: npx fqmpeg resize smaller.mp4 --height 1080(もう一方の高さに合わせる)。アスペクト比が違ってクロップしたくない時は、pad で一旦次元を揃えてから stack する手もある — border を使うと装飾フレームで埋める形にできるので簡易の代替になる。

border で出力サイズが非標準になるのは大丈夫?

アスペクト比に厳しいプラットフォーム(Instagram の 1:1、YouTube Shorts の 9:16)では問題になる。1920x1080--width 301980x1140 になり、多くのプレイヤーはレターボックス、一部の SNS は再クロップする。回避策は2つ:(1) 先に crop で内側動画を縮めてから border で元サイズに戻す;(2) プラットフォーム任せにしてレターボックス or クロップを許容する。

複数オーバーレイ動詞をチェインすると H.264 の世代劣化が積もる?

各 fqmpeg 呼び出しが再エンコードを伴うので積もる。緩和策2つ:(1) 中間ステップを -crf 18 やロスレスに引き上げる(--dry-run 出力を編集);(2) fqmpeg を経由せず、各動詞の --dry-run から取ったフィルタ文字列を -vf "filter1,filter2,filter3"(複数入力を含むなら -filter_complex)で1コマンドにまとめる。2-3パスなら実コンテンツでほぼ気づかないレベル、5パス超えると見えてくる。

まとめ

C8 の13動詞は、編集後・最終出力前の合成オペレーションをカバーする:

  • 静的グラフィック・テキスト (watermark, text, drawbox, timecode, border) — ロゴ・タイトル・ハイライト・タイムスタンプ・装飾フレームの焼き付け
  • 複数動画の合成 (pip, pip-grid, blend, stack) — ピクチャ・イン・ピクチャ、2-9入力グリッド、不透明度ブレンド、Before/After 並べ
  • 静止画→動画 (picture) — 静止画 + 音声を再生可能な MP4 に
  • 解析オーバーレイ (video-info-overlay, histogram-overlay, progress) — フレームメタ情報・カラースコープ・進捗バーをレビュー用に焼き付け

各動詞は --dry-run で内部の FFmpeg 呼び出しを表示するので、デフォルトが合わない時(histogram-overlay の位置をカスタムしたい、drawtext のフォーマットを変えたい、世代劣化を避けるため1パスでチェインしたい等)はコマンドをコピーして編集し、FFmpeg を直接叩くのが筋。fqmpeg 全体像は fqmpeg 完全ガイド を参照。