FFmpeg lavfi マスターガイド:フィルターグラフで高度な加工を実現
FFmpegは、デジタルメディア処理の分野でデファクトスタンダードとも言える強力なツールです。動画、音声、字幕など、さまざまなフォーマットの変換、ストリーミング、再生、そして何よりも重要な加工をコマンドライン一つで実現します。FFmpegの多様な機能の中でも、特に強力で柔軟性が高いのがlibavfilter
モジュール、通称lavfi
です。そして、このlavfi
の真髄を引き出すのがフィルターグラフです。
この記事では、FFmpegのlavfi
フィルターグラフに焦点を当て、その基本的な考え方から高度な応用までを詳細に解説します。単なるフィルターの適用を超えた、複雑なメディア加工を実現するための知識と実践的なテクニックを習得することを目標とします。
1. FFmpegとlavfiの概要
まずは、FFmpegとlavfi
が何であるかを簡単に理解しましょう。
1.1 FFmpegとは
FFmpegは、GPL/LGPLライセンスの下で開発されているオープンソースのマルチメディアフレームワークです。コマンドラインツール、ライブラリ群(libavcodec, libavformat, libavutil, libavfilter, libavdevice, libswscale, libswresample)から構成されており、ほぼ全ての主要なメディアフォーマットに対応しています。
- 変換 (Transcoding): MP4からWebMへ、AACからMP3へなどのフォーマット変換。
- ストリーミング (Streaming): ネットワークを介したメディアの送受信。
- 再生 (Playback): メディアファイルの再生(
ffplay
ツール)。 - 録画 (Recording): デバイスからの入力をファイルに保存。
- 加工 (Processing): 解像度変更、トリミング、色調補正、エフェクト適用、音声ミキシングなど。
この記事で扱うのは、この「加工」の部分、特にlavfi
を使った高度な加工です。
1.2 libavfilter (lavfi) とは
libavfilter
は、FFmpegのライブラリ群の一つで、メディアデータ(映像フレームや音声サンプル)に対するフィルタリング処理を担当します。フィルタリングとは、入力されたデータに対して何らかの変換を行い、新しいデータを出力する処理のことです。
例えば、映像をリサイズする「scale」フィルター、音量を調整する「volume」フィルターなどがあります。FFmpegコマンドラインでは、-vf
オプション(映像フィルター)や-af
オプション(音声フィルター)を使ってこれらのフィルターを適用できます。
“`bash
例:映像を半分のサイズにリサイズ
ffmpeg -i input.mp4 -vf scale=iw/2:ih/2 output.mp4
例:音声の音量を2倍にする
ffmpeg -i input.mp4 -af volume=2 output.mp4
“`
これらの例は「フィルターチェーン」と呼ばれるものです。一つのフィルター、あるいは複数のフィルターを直列につないで、入力ストリームを処理し、最終的な出力ストリームを生成します。これは最も基本的なlavfi
の利用方法です。
2. フィルターグラフの概念
フィルターチェーンはシンプルですが、複雑な処理には限界があります。例えば、複数の映像を合成したり、一つの映像を分割して異なる処理を施したり、音声と映像を組み合わせた複雑なエフェクトを適用したりといったことは、単純なチェーンでは実現できません。
そこで登場するのがフィルターグラフです。
2.1 フィルターチェーン vs. フィルターグラフ
-
フィルターチェーン (Filterchain): 一つの入力ストリームに対して、フィルターを直列に連結していく処理の流れです。入力は1つ、出力は1つです。
-vf
や-af
で指定します。
例:[input_video] -> [scale] -> [crop] -> [output_video]
-
フィルターグラフ (Filtergraph): 一つ以上の入力ストリームに対して、フィルターをノードとして、ストリームをエッジとして接続した処理のネットワークです。複数の入力、複数の出力、並列処理、分岐、合流、フィードバックループなど、複雑な処理経路を構築できます。
-filter_complex
オプションで指定します。
例:
“`
[input1_video] -> [scale] -\
> [overlay] -> [output_video]
[input2_video] -> [crop] –/[input1_audio] -> [volume] -\
> [amix] -> [output_audio]
[input2_audio] -> [adelay] -/
“`
フィルターグラフは、あたかもレゴブロックのようにフィルターを自由に組み合わせ、複雑なデータフローを構築できる強力なメカニズムです。
2.2 フィルターグラフの構成要素
フィルターグラフは、以下の要素で構成されます。
- 入力ストリーム (Input Streams): FFmpegの入力ファイルから供給される元のメディアストリーム(映像、音声など)です。
[0:v]
(1番目の入力ファイルの映像ストリーム)、[1:a]
(2番目の入力ファイルの音声ストリーム)のように指定されます。 - フィルター (Filters): データに対する具体的な処理を行うノードです(例:
scale
,volume
,overlay
,amix
)。 - パッド (Pads): フィルターの入力または出力ポートです。
- 入力パッド (Input Pads): フィルターがデータを受け取るポートです。
- 出力パッド (Output Pads): フィルターが処理済みデータを出力するポートです。
- 多くのフィルターは入力パッドを1つ、出力パッドを1つ持ちますが、中には複数の入出力パッドを持つフィルターもあります(例:
split
は入力1つ、出力複数、amix
は入力複数、出力1つ)。
- ストリーム接続 (Stream Connections): あるフィルターの出力パッドと、別のフィルターの入力パッドを接続する線です。これによりデータの流れが定義されます。
- ストリームラベル (Stream Labels): フィルターの入出力パッドに付ける名前です。
[mylabel]
のように角括弧で囲んで指定します。これにより、グラフ内の特定のポイントを明確に識別し、複雑な接続を記述できます。ラベルがない場合は、自動的にデフォルトのラベルが割り当てられるか、直前の出力が次の入力に暗黙的に接続されます(チェーンの場合)。 - ソースフィルター (Source Filters): 入力ストリームを必要とせず、自分でデータを生成するフィルターです(例:
color
、testsrc
、smptebars
、anullsrc
)。フィルターグラフの開始点となります。 - シンクフィルター (Sink Filters): 処理済みのデータを受け取り、グラフから外部(通常はFFmpegの出力ファイル)へ渡すフィルターです。入力パッドを持ちますが、出力パッドはありません(例:
buffersink
)。フィルターグラフの終端となります。
2.3 フィルターグラフのデータフロー
フィルターグラフ内では、データは「フレーム」(映像の場合は1フレーム、音声の場合はサンプルチャンク)単位で流れます。FFmpegは、グラフ内の各フィルターが必要な入力フレームを取得し、処理を行い、出力フレームを次のフィルターに渡すという処理を効率的に実行します。
重要なのは、フィルターグラフは入力ファイル全体の読み込みと処理を待つのではなく、ストリーム処理的に動作するということです。入力からフレームが供給されると、それがグラフを通って処理され、出力に書き出されていきます。
3. フィルターグラフの基本構文 (-filter_complex
)
フィルターグラフを定義する際は、-filter_complex
オプションを使用します。このオプションの値として、フィルターグラフの記述文字列を指定します。
基本的な構文は以下のようになります。
-filter_complex "グラフ記述文字列"
グラフ記述文字列は、一つ以上の「フィルター記述」をセミコロン ;
で区切って並べたものです。
"[入力ラベル]フィルター名=オプション[出力ラベル]; [入力ラベル]フィルター名=オプション[出力ラベル]; ..."
3.1 各要素の詳細
-
[入力ラベル]
: そのフィルターの入力となるストリームのラベルです。- FFmpegの入力ファイルからのストリームを指定する場合は、
[ファイルインデックス:ストリーム種別:ストリームインデックス]
の形式になります。ファイルインデックス
: 0から始まる入力ファイルの番号。ストリーム種別
:v
(映像),a
(音声),s
(字幕),d
(データ),t
(添付ファイル)。ストリームインデックス
: そのファイル内の同じ種別のストリーム番号(省略可能、デフォルトは0)。- 例:
[0:v:0]
(1番目のファイルの1番目の映像ストリーム)、[1:a]
(2番目のファイルのデフォルト音声ストリーム)。
- 他のフィルターの出力ストリームを指定する場合は、そのフィルターの出力パッドに付けたラベルを使用します。例:
[scaled_video]
。 - 入力ラベルを省略した場合、そのフィルターは直前のフィルターチェーンの出力ストリーム(ラベルが付いていない場合)または明示的に指定された入力を持たないフィルター(最初のフィルターなど)に接続されます。しかし、複雑なグラフでは明示的なラベル付けが推奨されます。
- FFmpegの入力ファイルからのストリームを指定する場合は、
-
フィルター名
: 適用するフィルターの名前です。例:scale
,volume
,overlay
。 -
=オプション
: フィルターのオプションを指定します。- オプションが複数ある場合は、コロン
:
で区切ります。例:scale=width=1280:height=720
。 - オプション名と値は
=
で結びます。 - オプション値に特殊文字(空白、
,
:
=
;
\
'
"
[
]
~
#
$
&
*
(
)
|
<
>
{
}
)を含む場合は、適切にエスケープするか引用符で囲む必要があります。通常はシングルクォート'
またはダブルクォート"
で全体を囲み、さらに内部の特殊文字はバックスラッシュ\
でエスケープします。-filter_complex
文字列全体をダブルクォートで囲むことが多いので、内部のオプション値はシングルクォートで囲むか、特殊文字をバックスラッシュでエスケープします。
例:drawtext=text='Hello\, World!':fontfile=/path/to/font.ttf
- 多くのオプション値では、入力や出力のプロパティ(幅
iw
/ow
、高さih
/oh
、アスペクト比ia
/oa
、フレームレートir
/or
、現在のタイムスタンプt
など)や数学的な計算式を使用できます。例:scale=iw/2:ih/2
(入力幅/2 : 入力高さ/2)。
- オプションが複数ある場合は、コロン
-
[出力ラベル]
: そのフィルターの出力ストリームに付けるラベルです。このラベルは、後続のフィルターの入力として参照できます。- 出力ラベルを省略した場合、そのフィルターの出力は、特に指定がない限り、グラフ記述文字列の中で次に現れるフィルターの入力として使用されます。
- 複数の出力パッドを持つフィルターの場合、それぞれの出力パッドにラベルを付ける必要があります。例:
[in]split[out1][out2]
は、入力[in]
を2つの出力[out1]
と[out2]
に分割します。
-
;
: 一つのフィルター記述の終わりを示し、次のフィルター記述との区切りです。これにより、複数の独立した、または相互接続されたフィルターチェーンを一つのフィルターグラフ内に記述できます。
3.2 シンプルなフィルターグラフの例
シンプルなリサイズを-filter_complex
で記述してみましょう。
bash
ffmpeg -i input.mp4 -filter_complex "[0:v]scale=640:480[outv]" -map "[outv]" output.mp4
[0:v]
:1番目の入力ファイルの映像ストリームを指定します。scale=640:480
:scale
フィルターを適用し、幅を640、高さを480に指定します。[outv]
:scale
フィルターの出力ストリームにoutv
というラベルを付けます。-map "[outv]"
:FFmpegの出力ファイルに、このフィルターグラフの[outv]
というラベルが付いたストリームを使用することを指定します。
この例では、入力ストリーム[0:v]
がscale
フィルターに入り、処理結果が[outv]
というラベルで出力されます。これは見た目には-vf
を使うのとあまり変わりませんが、これが複雑なグラフの基本的な構成要素となります。
音声も同様に扱えます。
bash
ffmpeg -i input.mp4 -filter_complex "[0:a]volume=2[outa]" -map "[outa]" output.mp4
映像と音声の両方を処理する場合は、セミコロンで区切って複数のフィルター記述を並べます。
bash
ffmpeg -i input.mp4 -filter_complex "[0:v]scale=640:480[outv]; [0:a]volume=2[outa]" -map "[outv]" -map "[outa]" output.mp4
この例では、映像ストリーム[0:v]
に対するscale
フィルターチェーン(出力[outv]
)と、音声ストリーム[0:a]
に対するvolume
フィルターチェーン(出力[outa]
)が、フィルターグラフ内で並列に実行されます。これらのチェーンは互いに独立しており、-map
オプションでそれぞれの最終出力をFFmpegの出力ファイルにマッピングしています。
3.3 暗黙的な接続
フィルターグラフの記述において、入力ラベルや出力ラベルを省略した場合、FFmpegはいくつかのルールに基づいて自動的に接続を試みます。
- 入力ラベルの省略: あるフィルター記述で入力ラベルが省略された場合、それは通常、直前のフィルター記述の出力(ラベルがない場合)または最初の入力ストリーム(ファイルインデックス0の映像/音声)に接続されます。
- 出力ラベルの省略: あるフィルター記述で出力ラベルが省略された場合、その出力は後続のフィルター記述の入力(ラベルがない場合)に暗黙的に接続されます。
例:
“`bash
-vf scale=640:480:crop=640:400:0:0 と等価なフィルターチェーン
ffmpeg -i input.mp4 -filter_complex “scale=640:480,crop=640:400:0:0” -map 0:v output.mp4
[0:v]scale=640:480[s]; [s]crop=640:400:0:0[c] と等価
ffmpeg -i input.mp4 -filter_complex “[0:v]scale=640:480,crop=640:400:0:0” -map 0:v output.mp4
“`
上記の例では、scale=...
の入力ラベルと出力ラベル、および crop=...
の入力ラベルと出力ラベルが省略されています。
scale=...
は最初の入力ストリーム[0:v]
に接続され、その出力は暗黙的に crop=...
の入力に接続されます。crop=...
の出力は、最終的に-map 0:v
(ここではフィルターグラフ出力全体のデフォルトストリームがマッピングされる)によって出力ファイルに送られます。
このように、カンマ ,
でフィルターを区切る記法は、単一入力・単一出力のフィルターチェーンを記述する際に便利です。これは-vf
や-af
オプションで使う記法と同じですが、-filter_complex
内でも使えます。
ただし、複数の入力/出力を持つフィルターを使用する場合や、グラフが分岐・合流する場合は、明示的にラベルを付けることが必須となります。
4. 応用例:複雑なフィルターグラフの構築
ここからは、具体的な応用例を通して、フィルターグラフの強力さを体感していきます。
4.1 複数の映像を重ねる (Picture-in-Picture)
一つの映像の上に別の映像を重ねて表示する、いわゆるPicture-in-Picture (PiP) を実現します。
入力:
* main.mp4
: メインとなる映像(例: 1280×720)
* overlay.mp4
: 小窓として重ねる映像(例: 640×360)
目標: main.mp4
の右下隅に、リサイズしたoverlay.mp4
を重ねる。音声は両方ミックスする。
必要なフィルター:
* 映像: scale
(overlay用), overlay
* 音声: amix
フィルターグラフの設計:
1. main.mp4
の映像 ([0:v]
) はそのままoverlay
フィルターの「メイン入力」に送る。
2. overlay.mp4
の映像 ([1:v]
) をscale
フィルターで小窓サイズにリサイズする。
3. リサイズされた映像 ([scaled_overlay]
) をoverlay
フィルターの「オーバーレイ入力」に送る。
4. overlay
フィルターの出力 ([outv]
) を最終的な映像出力とする。
5. main.mp4
の音声 ([0:a]
) と overlay.mp4
の音声 ([1:a]
) をamix
フィルターに入力する。
6. amix
フィルターの出力 ([outa]
) を最終的な音声出力とする。
グラフ記述文字列:
"[0:v]split[mainv][dummyv]; \
[1:v]scale=320:180[overlayv]; \
[mainv][overlayv]overlay=W-w-10:H-h-10[outv]; \
[0:a][1:a]amix[outa]"
※ split[mainv][dummyv]
は、厳密には必要ありませんが、overlayフィルターのメイン入力にラベル付きストリームを渡すための一般的なパターンです。省略して [0:v][overlayv]overlay=...[outv]
と書いても動作します。ここでは、メイン映像を明示的に扱うためにsplit
を使用します。
コマンド:
bash
ffmpeg -i main.mp4 -i overlay.mp4 -filter_complex \
"[0:v]split[mainv][dummyv]; \
[1:v]scale=320:180[overlayv]; \
[mainv][overlayv]overlay=x=W-w-10:y=H-h-10[outv]; \
[0:a][1:a]amix[outa]" \
-map "[outv]" -map "[outa]" output_pip.mp4
解説:
* -i main.mp4 -i overlay.mp4
: 入力ファイルは2つです。それぞれファイルインデックス0と1になります。
* [0:v]split[mainv][dummyv]
: 1番目の入力の映像ストリーム[0:v]
をsplit
フィルターで分割します。1つ目の出力に[mainv]
というラベルを付けます。2つ目の出力[dummyv]
は今回は使いませんが、split
は入力数を指定しない場合、デフォルトで2つ出力パッドを作成します。
* [1:v]scale=320:180[overlayv]
: 2番目の入力の映像ストリーム[1:v]
をscale
フィルターで320×180にリサイズし、出力に[overlayv]
というラベルを付けます。
* [mainv][overlayv]overlay=x=W-w-10:y=H-h-10[outv]
: overlay
フィルターは入力パッドを2つ持ちます。1つ目はメインの映像[mainv]
、2つ目はオーバーレイする映像[overlayv]
です。
* x=W-w-10
: オーバーレイ映像の左上隅のX座標を指定します。メイン映像の幅(W
)からオーバーレイ映像の幅(w
)を引き、さらに右端から10ピクセル内側に入った位置です。
* y=H-h-10
: 同様にY座標を指定します。メイン映像の高さ(H
)からオーバーレイ映像の高さ(h
)を引き、下端から10ピクセル内側に入った位置です。これにより、右下隅に配置されます。
* [outv]
: overlay
フィルターの出力に[outv]
というラベルを付けます。
* [0:a][1:a]amix[outa]
: amix
フィルターは入力パッドを複数持ち(デフォルトは2)、全ての入力をミックスします。ここでは1番目の入力の音声[0:a]
と2番目の入力の音声[1:a]
を入力として指定し、出力に[outa]
というラベルを付けます。
* -map "[outv]" -map "[outa]"
: フィルターグラフの出力ストリーム[outv]
と[outa]
を、FFmpegの出力ファイルoutput_pip.mp4
の映像ストリームと音声ストリームとして使用するように指定します。
このように、フィルターグラフを使えば、複数の入力を扱い、それらを異なるフィルターで処理し、最終的に一つの出力に結合するといった複雑な処理も直感的に記述できます。
4.2 複数の映像を並べて表示 (Tiling)
複数の映像をタイル状に並べて表示する例です。
入力:
* video1.mp4
* video2.mp4
* video3.mp4
* video4.mp4
目標: 4つの映像を2×2のグリッド状に並べる。音声は全てミックスする。
必要なフィルター:
* 映像: scale
(全て同じサイズにするため), xstack
(またはhstack
/vstack
)
* 音声: amix
フィルターグラフの設計:
1. 4つの入力映像ストリーム ([0:v]
, [1:v]
, [2:v]
, [3:v]
) を、全て同じサイズ(例: 640×360)にscale
する。それぞれの出力にラベルを付ける ([v1]
, [v2]
, [v3]
, [v4]
)。
2. リサイズされた4つの映像ストリーム[v1]
, [v2]
, [v3]
, [v4]
をxstack
フィルターに入力する。
3. xstack
フィルターで2×2のレイアウトを指定し、出力に[outv]
というラベルを付ける。
4. 4つの入力音声ストリーム ([0:a]
, [1:a]
, [2:a]
, [3:a]
) をamix
フィルターに入力する。
5. amix
フィルターの出力 ([outa]
) を最終的な音声出力とする。
グラフ記述文字列:
"[0:v]scale=640:360[v1]; \
[1:v]scale=640:360[v2]; \
[2:v]scale=640:360[v3]; \
[3:v]scale=640:360[v4]; \
[v1][v2][v3][v4]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[outv]; \
[0:a][1:a][2:a][3:a]amix=inputs=4[outa]"
コマンド:
bash
ffmpeg -i video1.mp4 -i video2.mp4 -i video3.mp4 -i video4.mp4 -filter_complex \
"[0:v]scale=640:360[v1]; \
[1:v]scale=640:360[v2]; \
[2:v]scale=640:360[v3]; \
[3:v]scale=640:360[v4]; \
[v1][v2][v3][v4]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[outv]; \
[0:a][1:a][2:a][3:a]amix=inputs=4[outa]" \
-map "[outv]" -map "[outa]" output_tiled.mp4
解説:
* -i video1.mp4 ...
: 4つの入力ファイルです。ファイルインデックス0, 1, 2, 3となります。
* [0:v]scale=...[v1]; ... [3:v]scale=...[v4]
: 4つの映像ストリームそれぞれを640×360にリサイズし、[v1]
から[v4]
のラベルを付けます。
* [v1][v2][v3][v4]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[outv]
: xstack
フィルターは複数の映像入力を取ります。
* inputs=4
: 入力が4つであることを明示します(デフォルトも4ですが、明示すると分かりやすい)。入力パッドは指定された順番になります ([v1]
が0番目、[v2]
が1番目、…)。
* layout=0_0|w0_0|0_h0|w0_h0
: 2×2のレイアウトを定義します。x_y
の形式で、各入力映像の左上隅の座標を指定します。|
で区切ります。座標には、入力映像の幅w<idx>
や高さh<idx>
、または出力映像の幅W
や高さH
といった変数を使用できます。
* 0_0
: 1番目の映像 ([v1]
) を (0, 0) に配置。
* w0_0
: 2番目の映像 ([v2]
) を (1番目の映像の幅, 0) に配置。つまり1番目の右隣。
* 0_h0
: 3番目の映像 ([v3]
) を (0, 1番目の映像の高さ) に配置。つまり1番目の真下。
* w0_h0
: 4番目の映像 ([v4]
) を (1番目の映像の幅, 1番目の映像の高さ) に配置。つまり1番目の右下の位置。
* [outv]
: xstack
フィルターの出力に[outv]
というラベルを付けます。
* [0:a][1:a][2:a][3:a]amix=inputs=4[outa]
: 4つの入力音声ストリームをamix
フィルターでミックスします。inputs=4
で入力数を指定し、出力に[outa]
というラベルを付けます。
* -map "[outv]" -map "[outa]"
: 最終出力を指定します。
hstack
とvstack
は、それぞれ横方向、縦方向のみにスタックする場合に使います。xstack
はより柔軟なグリッドレイアウトに対応しています。
4.3 映像にテキストオーバーレイと簡単な色調補正
映像にテキストを焼き付け、さらに明るさを少し調整する例です。
入力: input.mp4
目標: 映像の左上隅にテキストを表示し、明るさを+0.1だけ補正する。
必要なフィルター:
* 映像: drawtext
, colorbalance
フィルターグラフの設計:
1. 入力映像ストリーム ([0:v]
) をdrawtext
フィルターに入力する。
2. drawtext
フィルターの出力 ([texted]
) をcolorbalance
フィルターに入力する。
3. colorbalance
フィルターの出力 ([outv]
) を最終的な映像出力とする。
4. 入力音声ストリーム ([0:a]
) はそのまま最終的な音声出力とする(加工しない)。
グラフ記述文字列:
"[0:v]drawtext=text='FFmpeg Filtergraph':x=10:y=10:fontsize=24:fontcolor=white[texted]; \
[texted]colorbalance=bs=0.1[outv]"
コマンド:
bash
ffmpeg -i input.mp4 -filter_complex \
"[0:v]drawtext=text='FFmpeg Filtergraph':x=10:y=10:fontsize=24:fontcolor=white:fontfile='/path/to/font.ttf'[texted]; \
[texted]colorbalance=bs=0.1[outv]" \
-map "[outv]" -map 0:a output_processed.mp4
解説:
* -i input.mp4
: 入力ファイルは1つです。
* [0:v]drawtext=...[texted]
: 入力映像[0:v]
にdrawtext
フィルターを適用します。
* text='FFmpeg Filtergraph'
: 表示するテキスト。特殊文字を含む可能性があるためシングルクォートで囲んでいます。
* x=10:y=10
: 左上隅から10ピクセルオフセットした位置に表示。
* fontsize=24
: フォントサイズ。
* fontcolor=white
: フォントの色。
* fontfile='/path/to/font.ttf'
: 使用するフォントファイルのパス。これはシステムによって異なりますので、適切なパスを指定してください。
* [texted]
: drawtext
の出力に[texted]
というラベルを付けます。
* [texted]colorbalance=bs=0.1[outv]
: drawtext
の出力[texted]
をcolorbalance
フィルターに入力します。
* bs=0.1
: シャドーの明るさ (brightness) を+0.1増やすオプションです。他のオプション(ハイライトhs
、ミッドトーンms
、RGBバランスrc
, gc
, bc
など)もあります。
* [outv]
: colorbalance
の出力に[outv]
というラベルを付けます。これが最終的な映像出力です。
* -map "[outv]" -map 0:a
: フィルターグラフの映像出力[outv]
を使用し、入力ファイル0の元の音声ストリーム0:a
をそのまま出力に使用します。音声加工を行わない場合は、このように元のストリームをマッピングするのが一般的です。
この例では、フィルターチェーンのように、あるフィルターの出力が次のフィルターの入力になるという流れが明確に分かります。ラベルを使うことで、この流れを明示的に指定しています。
4.4 複雑なストリームルーティングの例 (split
, merge
)
一つの入力映像を異なる方法で処理し、それらを合成する例です。例えば、映像の一部を拡大表示し、元の映像の一部にオーバーレイするといったケースです。
入力: input.mp4
目標:
1. 入力映像を2つのストリームにsplit
する。
2. 片方のストリーム ([mainv]
) はそのまま使うか、軽く加工する(例: コントラスト調整)。
3. もう片方のストリーム ([detailv]
) は、特定領域をcrop
し、scale
で拡大する。
4. 拡大した映像を、元の映像 ([mainv]
) の上にoverlay
する。
必要なフィルター:
* 映像: split
, crop
, scale
, overlay
, geq
(コントラスト調整用)
フィルターグラフの設計:
1. 入力映像 ([0:v]
) をsplit
フィルターで2つに分割し、[mainv]
と[detailv_in]
のラベルを付ける。
2. [mainv]
をgeq
フィルター(コントラスト調整)に通し、出力に[mainv_processed]
のラベルを付ける。
3. [detailv_in]
をcrop
フィルターで特定領域を切り出す。
4. crop
の出力 ([cropped_detail]
) をscale
フィルターで拡大し、出力に[detailv_scaled]
のラベルを付ける。
5. [mainv_processed]
(メイン映像)と[detailv_scaled]
(拡大映像)をoverlay
フィルターに入力し、出力に[outv]
のラベルを付ける。
6. 入力音声 ([0:a]
) はそのまま出力する。
グラフ記述文字列:
"[0:v]split[mainv][detailv_in]; \
[mainv]geq=contrast=1.2[mainv_processed]; \
[detailv_in]crop=320:240:100:100[cropped_detail]; \
[cropped_detail]scale=640:480[detailv_scaled]; \
[mainv_processed][detailv_scaled]overlay=x=W-w-10:y=H-h-10[outv]"
コマンド:
bash
ffmpeg -i input.mp4 -filter_complex \
"[0:v]split[mainv][detailv_in]; \
[mainv]geq=contrast=1.2[mainv_processed]; \
[detailv_in]crop=320:240:100:100[cropped_detail]; \
[cropped_detail]scale=640:480[detailv_scaled]; \
[mainv_processed][detailv_scaled]overlay=x=W-w-10:y=H-h-10[outv]" \
-map "[outv]" -map 0:a output_routed.mp4
解説:
* [0:v]split[mainv][detailv_in]
: 入力映像を[mainv]
と[detailv_in]
の2つのストリームに分岐させます。
* [mainv]geq=contrast=1.2[mainv_processed]
: [mainv]
ストリームをgeq
フィルターでコントラストを1.2倍に調整し、[mainv_processed]
として出力します。
* [detailv_in]crop=320:240:100:100[cropped_detail]
: [detailv_in]
ストリームに対してcrop
フィルターを適用します。幅320、高さ240の領域を、元の映像の左上隅からX座標100、Y座標100の位置から切り出します。出力は[cropped_detail]
。
* [cropped_detail]scale=640:480[detailv_scaled]
: 切り出した映像[cropped_detail]
をscale
フィルターで640×480に拡大し、[detailv_scaled]
として出力します。
* [mainv_processed][detailv_scaled]overlay=...[outv]
: 処理済みのメイン映像[mainv_processed]
の上に、拡大した詳細映像[detailv_scaled]
を重ね合わせます。配置は右下隅(例としてPiPと同じ座標を使用)。出力は[outv]
。
この例は、split
でストリームを分岐させ、それぞれ独立した処理を行い、overlay
で再び合流させるという、フィルターグラフならではの複雑なデータフローを示しています。
4.5 ソースフィルターとシンクフィルターの使用
外部の入力ファイルだけでなく、FFmpeg自体が生成するテストパターンやカラーバー、無音などのストリームをフィルターグラフの入力として使用できます。これらをソースフィルターと呼びます。逆に、フィルターグラフの最終出力をFFmpegのエンコーダーや出力ファイルに渡す役割をするのがシンクフィルターですが、-filter_complex
で指定した最終出力ラベルは、FFmpegの内部的なシンクフィルター(buffersink
など)に自動的に接続されるため、通常は明示的に記述する必要はありません。しかし、グラフの仕組みを理解する上で重要です。
例:単色の背景映像を生成し、その上に別の映像を重ねる
入力: overlay.mp4
目標: 解像度1280×720の青い単色背景を60秒間生成し、その中央にoverlay.mp4
の映像を重ねる。音声はoverlay.mp4
のものを使用する。
必要なフィルター:
* 映像: color
(ソースフィルター), overlay
, scale
(overlay用)
* 音声: なし(入力音声を使用)
フィルターグラフの設計:
1. color
ソースフィルターで、1280×720の青い映像を60秒間生成し、出力に[background]
のラベルを付ける。
2. overlay.mp4
の映像 ([0:v]
) を、必要に応じてscale
し、出力に[overlayv]
のラベルを付ける。
3. [background]
(メイン映像)と[overlayv]
(オーバーレイ映像)をoverlay
フィルターに入力し、出力に[outv]
のラベルを付ける。
4. overlay.mp4
の音声 ([0:a]
) をそのまま最終音声出力とする。
グラフ記述文字列:
"color=c=blue:s=1280x720:d=60[background]; \
[0:v]scale='min(iw,W*0.8)':'min(ih,H*0.8)'[overlayv]; \
[background][overlayv]overlay=(W-w)/2:(H-h)/2[outv]"
コマンド:
bash
ffmpeg -i overlay.mp4 -filter_complex \
"color=c=blue:s=1280x720:d=60[background]; \
[0:v]scale='min(iw,W*0.8)':'min(ih,H*0.8)'[overlayv]; \
[background][overlayv]overlay=x=(W-w)/2:y=(H-h)/2[outv]" \
-map "[outv]" -map 0:a output_color_overlay.mp4
解説:
* -i overlay.mp4
: 入力ファイルは1つです(ファイルインデックス0)。
* color=c=blue:s=1280x720:d=60[background]
: color
ソースフィルターを実行します。
* c=blue
: 色を青に指定。色の名前、RGB値、またはHTMLカラーコードで指定可能。
* s=1280x720
: 生成する映像のサイズを指定。
* d=60
: 生成する映像の秒数を指定。
* [background]
: 出力に[background]
のラベルを付けます。これは入力ストリームを持たない、グラフの開始点です。
* [0:v]scale='min(iw,W*0.8)':'min(ih,H*0.8)'[overlayv]
: 入力ファイル0の映像[0:v]
をscale
フィルターでリサイズします。
* 'min(iw,W*0.8)':
入力幅iw
と、背景映像の幅W
の80%のうち小さい方になるように幅を決定します。これにより、オーバーレイ映像が背景より常に小さくなるようにします。
* 'min(ih,H*0.8)':
同様に高さを決定します。
* [overlayv]
: 出力に[overlayv]
のラベルを付けます。
* [background][overlayv]overlay=x=(W-w)/2:y=(H-h)/2[outv]
: [background]
の上に[overlayv]
を重ねます。
* x=(W-w)/2:y=(H-h)/2
: オーバーレイ映像を背景映像の中央に配置します。メイン映像の幅W
からオーバーレイ映像の幅w
を引いた値の半分がX座標、メイン映像の高さH
からオーバーレイ映像の高さh
を引いた値の半分がY座標になります。
* [outv]
: 出力に[outv]
のラベルを付けます。
* -map "[outv]" -map 0:a
: 最終出力を指定します。
このように、ソースフィルターを使うことで、FFmpegのコマンドラインだけで、外部ファイルに依存しない映像要素(背景、テストパターン、ノイズなど)をグラフに組み込むことができます。
5. よく使うフィルターの種類
フィルターグラフでよく使われる代表的なフィルターをいくつか紹介します。
5.1 映像フィルター
scale
: 解像度を変更します。width:height
または式で指定。例:scale=640:480
,scale=iw/2:ih/2
。crop
: 映像の一部を切り出します。width:height:x:y
で指定。例:crop=640:400:0:0
(左上から640×400)。pad
: 映像の周りにパディング(余白)を追加します。width:height:x:y:color
で指定。例:pad=1920:1080:(ow-iw)/2:(oh-ih)/2:black
(中央配置で黒い余白)。overlay
: 複数の映像を重ね合わせます。入力は2つ(メイン、オーバーレイ)。x:y
でオーバーレイ位置を指定。例:overlay=10:10
。split
: 入力ストリームを複数の同一のストリームに複製します。出力パッドの数で指定。例:split=3[v1][v2][v3]
。merge
: 複数の映像ストリームを一つのフレームに合成します(タイミングは同期されるが、フレーム内容は合成されない)。主にsplit
と組み合わせて、異なる処理を施した同じ映像を後の段階で結合する場合に使う。inputs
オプションで入力数を指定。hstack
,vstack
,xstack
: 複数の映像ストリームを横 (hstack
)、縦 (vstack
)、またはグリッド (xstack
) に並べて一つの映像にする。transpose
: 映像を回転または反転します。direction
オプションで指定 (0=90度回転&垂直反転, 1=90度回転, 2=90度回転&水平反転, 3=270度回転)。例:transpose=1
(90度回転)。rotate
: 任意の角度で映像を回転します。angle
オプションでラジアンまたは度数を指定。例:rotate=PI/2
(90度)。drawtext
: 映像にテキストを描画します。豊富なオプションあり。例:drawtext=text='Hello':x=10:y=10
。colorbalance
: 色バランス、明るさ、コントラストなどを調整。例:colorbalance=bs=0.1:ms=0.05:hs=-0.1
。curves
: トーンカーブで色調補正。例:curves=preset=increase_contrast
。hue
: 色相と彩度を調整。例:hue=h=90:s=2
。geq
: 画素ごとの計算に基づいた汎用的なフィルター。算術式を使って複雑な変換が可能。例:geq=g='0.5*g'
(緑成分を半減)。yadif
: インターレース解除。例:yadif
。delogo
: ロゴなどの指定領域をぼかして消す。例:delogo=x=10:y=10:w=100:h=50:show=0
。
5.2 音声フィルター
volume
: 音量を調整します。volume
オプションでリニア値、デシベル値などを指定。例:volume=2.0
,volume=3dB
。amix
: 複数の音声ストリームをミックスします。inputs
オプションで入力数を指定。例:amix=inputs=2
。amerge
: 複数のモノラル音声ストリームを一つのマルチチャンネルストリームに結合します。入力数を指定。例:amerge=inputs=2
(左右チャンネルに結合)。asplit
: 入力ストリームを複数の同一の音声ストリームに複製します。出力パッドの数で指定。例:asplit=2[a1][a2]
。adelay
: 音声ストリームに遅延を追加します。delays
オプションで遅延時間(ミリ秒)をカンマ区切りで指定。例:adelay=1000|1000
(全てのチャンネルを1秒遅延)。highpass
,lowpass
: ハイパス/ローパスフィルター。特定の周波数帯域をカット。例:highpass=f=200
(200Hz以下をカット)。acompressor
: 音声コンプレッサー。音量のばらつきを抑える。例:acompressor=level_in=0.8:ratio=4:attack=5:release=50:detection=rms
。aecho
: エコー効果を追加。例:aecho=0.8:0.9:1000:0.3
。afade
: フェードイン/フェードアウト効果を追加。例:afade=t=in:st=0:d=5
(開始から5秒かけてフェードイン)。anullsrc
: 無音音源を生成するソースフィルター。r
(サンプルレート),ch
(チャンネルレイアウト),d
(期間) を指定。例:anullsrc=r=44100:ch=stereo:d=10
。
6. フィルターグラフ構築のヒントとデバッグ
複雑なフィルターグラフを構築する際のヒントと、問題が発生した場合のデバッグ方法です。
6.1 グラフ構築のヒント
- 小さなステップで進める: いきなり最終形を目指さず、まずはシンプルなグラフで試す。例えば、複数の入力が必要なら、まずそれらを読み込んで単純に並べるだけ、といった具合です。
- 図を描く: 複雑なグラフの場合は、手書きやツールでフィルターとストリーム接続の図を描いてみると、構造が理解しやすくなります。
- ラベルを積極的に使う: 特に分岐や合流がある場合は、明示的にラベルを付けましょう。可読性が上がり、デバッグも容易になります。
- 一時的な出力を作成する (
split
と出力): グラフの中間段階の映像や音声を確認したい場合、その時点のストリームをsplit
で分岐させ、分岐したストリームを一時的な出力ファイルにマッピングすることで確認できます。
例:...[filtered_v]split=2[final_v][temp_v]; [temp_v]format=yuv420p[temp_out]; ... -map "[final_v]" -map ... -map "[temp_out]" temp.mp4
(ただし、format
フィルターなどを追加して、一時出力用のエンコーダーが扱える形式にする必要がある場合があります)。 - 変数や式を活用する:
scale=iw/2:ih/2
のように、入力/出力プロパティを参照したり、簡単な計算をしたりすることで、より柔軟なグラフを作成できます。overlay=x=(W-w)/2:y=(H-h)/2
のような中央配置も変数を使えば簡単です。 - タイムライン編集 (
enable
オプション): 多くのフィルターはenable
オプションを持ち、式の結果が真となるフレームや期間のみフィルターを適用できます。これにより、時間によって効果が変わる複雑なアニメーションやトランジションを実現できます。
例:fade=t=in:st=0:d=5:enable='gte(t,0)*lt(t,5)'
(最初の5秒間にフェードイン)
例:drawtext=...:enable='mod(t,5)<2'
(5秒ごとに最初の2秒だけテキストを表示)
6.2 デバッグ方法
- エラーメッセージを読む: FFmpegはエラーメッセージが比較的親切です。フィルター名の間違い、オプションの間違い、パッドの接続間違いなど、エラーメッセージが原因箇所を示唆していることが多いです。
- 構文のチェック:
-filter_complex
文字列内のセミコロン;
、カンマ,
、コロン:
、イコール=
、角括弧[]
、クォート'
/"
の対応が正しいかを確認します。特に、特殊文字を含むオプション値は慎重にクォートとエスケープを行います。 - フィルターの情報を確認する:
ffmpeg -filters
: 全てのフィルターリストとその簡単な説明を表示します。ffmpeg -h filter=filter_name
: 特定のフィルターの詳細なヘルプ(オプション、入力/出力パッドなど)を表示します。
- 入力ファイルの情報を確認する:
ffprobe input.mp4
コマンドで、入力ファイルのストリーム種別、インデックス、解像度、フレームレート、サンプルレートなどの詳細を確認できます。これが[0:v]
などが正しく参照できているか確認する手がかりになります。 - 最もシンプルなグラフから始める: 問題が発生しているグラフを、可能な限りシンプルな状態に戻し、少しずつ要素を追加して、どこで問題が発生するかを特定します。
- シェルによるエスケープの違いに注意:
-filter_complex
文字列はシェルによって解釈が異なります。Linux/macOSのBashとWindowsのCommand Prompt/PowerShellでは、クォートや特殊文字のエスケープ方法が異なる場合があります。問題が発生した場合、別のシェルで試すか、より厳密なエスケープ(例:-filter_complex '"..."'
または-filter_complex \"...\"
)を試みてください。
7. 高度なトピック (概観)
この記事はフィルターグラフの入門から応用までをカバーしましたが、lavfi
にはさらに高度な機能もあります。
- Frame Threading: 一部のフィルターは、
-filter_complex
オプションに-filter_threads N
を指定することで、フレームレベルの並列処理を有効にできます。これにより、マルチコアCPUでフィルター処理を高速化できる場合があります。 - Graph2dot:
ffmpeg -filter_complex "..." -f lavfi -i null -metadata:g graph_description | dot -Tpng -o graph.png
のようなコマンドを使うと、記述したフィルターグラフをdot言語形式で出力し、Graphvizなどのツールで視覚化できます。複雑なグラフの構造を理解するのに役立ちます。 - タイムスタンプと同期: フィルターグラフ内の各フレームはタイムスタンプ(PTS: Presentation Time Stamp)を持ちます。フィルターは通常、このタイムスタンプを適切に処理しますが、複数のストリームを結合するフィルター(
overlay
,amix
,xstack
など)では、入力ストリームのタイムスタンプに基づいて出力を同期します。setpts
,asetpts
,setdar
,setpts
,atempo
などのフィルターを使って、タイムスタンプ、フレームレート、アスペクト比、速度などを調整することも可能です。 - カスタムフィルター: C言語で独自のフィルターを作成し、FFmpegに組み込むことも可能ですが、これは高度なプログラミングスキルが必要です。
8. まとめ
FFmpegのlibavfilter
モジュールとフィルターグラフは、コマンドラインで複雑なメディア加工を実現するための非常に強力なツールです。単なるフィルターチェーンの直列処理を超え、複数の入力ストリームを扱ったり、ストリームを分岐・合流させたり、並列処理を行ったりといった柔軟な加工パイプラインを構築できます。
この記事では、フィルターグラフの基本概念、-filter_complex
オプションの構文、そして複数の入出力、ストリームルーティング、ソースフィルターの使用といった具体的な応用例を通して、その能力を解説しました。
フィルターグラフの学習は、多くのフィルターの機能を知ることと、それらをどのように組み合わせて目的の処理を実現するかという考え方を習得することにあります。最初は難しく感じるかもしれませんが、小さなグラフから始め、必要に応じて要素を追加していくことで、徐々に慣れていくことができます。FFmpegのドキュメント(特にffmpeg-filters
マニュアルと各フィルターのヘルプ)は非常に詳細なので、特定のフィルターのオプションを知りたい場合に参照すると良いでしょう。
FFmpegのlavfi
フィルターグラフをマスターすることで、これまでは専用の編集ソフトウェアが必要だったような高度なメディア加工も、コマンドラインから自動化・バッチ処理できるようになり、ワークフローが大きく広がります。ぜひ、様々なフィルターグラフの構築に挑戦してみてください。