ffmpeg ノート¶
ffmpeg のマニュアルを読み取る。コマンドパイプラインの構造、フィルターグラフ、ストリーム選択の仕組みについて述べる。また、ここで述べる事実には ffplay と共通するものがある。
コマンドの構造¶
ffmpeg のコマンドラインの書式は次のように定められている:
ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
要点はこういう感じだろうか:
全体オプション、入力、出力の順番に指定する必要がある。
入力も出力も複数指定することが可能だ。
入力も出力もファイルシステムのパスでもインターネット上の URL でもいい。
入力ファイルそれぞれの直前に
-i
を明示する必要がある。一方、出力ファイルにはそのようなものがない。
コマンド処理のパイプラインは次のグラフのように表される:
ffmpeg は libavformat ライブラリーを呼び出して入力ファイルを読み、それらからエンコードされたデータを含むパケットを得る。入力ファイルが複数ある場合もある。
エンコード済みパケットは、decoder に渡される。非圧縮フレームを生成し、フィルターによってさらに処理することができる。
フィルターの後、フレームは encoder に渡され、それらをエンコードし、エンコード済みパケットを出力する。
最後に、エンコード済みパケットは muxer に渡され、出力ファイルに書き出される。
フィルターグラフ¶
エンコードする前に libavfilter ライブラリーのフィルターを使って生の音声・映像フレームを処理することができる。フィルターの連鎖がフィルターグラフを形成する。ffmpeg はフィルターグラフを simple と complex の二種類に分類して扱う。利用者の立場からするとコマンドラインの書き方が変わってくる。後者のほうが文字通り複雑になる。
単純フィルターグラフ¶
入力と出力がそれぞれ一つずつあり、両方とも同じ型であるフィルターグラフを単純フィルターグラフ (simple graph) という。最初の図における decode と encode の間に段階を追加するだけで、それを表現することができる。
単純フィルターグラフは、ストリームごとにオプション -filter
で設定する。次のような別名もあるので覚えておく:
-filter:v
,-vf
-filter:a
,-af
例えば次のようなコマンドでは、オプション -vf yadif,scale=256:224
部分が単純フィルターグラフを構成している:
bash$ ffmpeg -i input.mp4 -vf yadif,scale=256:224 output.mp4
ここでは映像フィルター yadif
と scale
を順番に適用している。カンマ ,
でフィルターを区切って列挙するだけでいい。各フィルターには固有の引数を与えることもある(この例では scale
でそうしている)。どのフィルターオプションにおいても次の書式になると思っていい:
filtername=param1=arg1:param2=arg2:...
filtername=arg1:arg2:...
このコマンドを図式化したものを記す:
フレームのプロパティーを変更するフィルターもあるが、普通はフレームの内容には触れない。例えば、フィルター fps
はフレーム数を変更するものの、その内容には触れない。もう一つの例はフィルター setpts
だ。これはタイムスタンプを設定するだけで、それ以外はフレームを変更せずに通過させる。
複雑フィルターグラフ¶
入出力ストリームが複数あり得るものを複雑フィルターグラフ (complex filtergraph) という。模式的に書くと例えば:
複雑フィルターグラフはオプション -filter_complex
で構成する。その性質上、単一ストリームやファイルに一義的に関連付けることができないため、このオプションは
global だ。
オプション -lavfi
は -filter_complex
と同じだ。
複雑なフィルターグラフの明らかな例はフィルター overlay
だ。二つのビデオ入力と一つのビデオ出力を持ち、一方のビデオが他方のビデオの上に重なっているものだ。その音声版に相当するのがフィルター amix
だ。例については別途述べる。
ストリーム copy
¶
ストリームコピーとはオプション -codec: copy
で機能するモードだ。意味としては基本図式の decoder と encoder の段階を省き、demuxer と muxer のみを機能させるということだ。用途としては、コンテナー形式を変換したり、コンテナーレベルのメタデータを変更したりするものだ。この状況での ffmpeg コマンドパイプラインの基本図式は次のように単純になる:
符号化や復号化処理がないため、たいへん高速で品質劣化もない。フィルターを適用することは当然不可能だ(フィルターは非圧縮データに対して機能するものだ)。
ストリーム選択¶
オプション -map
の値として指定する記号を理解するのにこの知識が要る。
ffmpeg には各出力ファイルのストリーム選択を手動で制御するためのオプション -map
がある。しかし、利用者は -map
を明示的に与えるのを省略して、以下に述べられるように ffmpeg 自身にストリームを自動的に選択させることができる。オプション
-vn
,-an
,-sn
,-dn
は、複雑なフィルタグラフー出力であるストリームを除き、手動で -map
されたか自動的に選択されたかにかかわらず、映像、音声、字幕、データストリームを含めるのを省略するのに使用できる。例えば -vn
を与えると、入力映像ストリームを一切無視する。
自動ストリーム選択¶
特定の出力ファイルにオプション -map
がない場合、ffmpeg は出力フォーマットを検査し、映像、音声、字幕など、どの種類のストリームを含められるかを検査する。許容されるストリーム種別ごとに、入力すべてから一つのストリームを(利用可能である限り)選択する。
ストリーム選択基準は次に基づく:
映像は最も解像度の高いストリーム
音声は最も多くのチャンネルを含むストリーム
字幕は、最初に発見した副字幕ストリームになる。注意:出力形式の既定の字幕エンコーダーは、文字ベースか画像ベースのどちらかで、同じ種類の字幕ストリームだけが選択される。
同種のストリームで等しく評価されるものが複数ある場合、最低インデックスストリームが選択される。
データまたは添付ストリームに関しては自動的に選択されることはない。オプション
-map
指定時にしか採用されない。
手動ストリーム選択¶
オプション -map
が使用された場合、ユーザーマップストリームしか出力ファイルに含まれない。ただし、以下で述べられるフィルターグラフ出力に関する例外がある。
複雑フィルターグラフ¶
ラベルのないパッドを持つ複雑なフィルターグラフ出力ストリームがある場合、それらは最初の出力ファイルに追加される。これは、そのストリーム種別が出力フォーマットで対応されていない場合、致命的なエラーになる。
オプション
-map
がない場合、これらのストリームを含めると、その種別のストリーム自動選択が飛ばされることになる。オプション
-map
が存在する場合、マップされたストリームに加えて、これらのフィルターグラフストリームが含まれる。
ラベル付きパッドを持つ複雑なフィルターグラフ出力ストリームは、一度だけ、正確にマップされなければならない。
Todo
パッドとは?
ストリーム処理¶
ストリーム処理はストリーム選択に依存しない(例外は字幕)。ストリーム処理は、特定の出力ファイル内のストリームに指定されたオプション -codec
によって設定される。特に、当オプションはストリーム選択処理の後に ffmpeg が適用するため、後者に影響を与えない。オプション -codec
が指定されていないストリーム種別に対しては、ffmpeg は出力ファイルの muxer によって登録された既定の
encoder を選択する。
ストリーム選択の例¶
以降の例は ffmpeg のストリーム選択方法の動作、癖、制限を説明するためのものだ。いずれも次のような入力ファイルがあるとする。この三つすべてを入力とする:
input file 'A.avi'
stream 0: video 640x360
stream 1: audio 2 channels
input file 'B.mp4'
stream 0: video 1920x1080
stream 1: audio 2 channels
stream 2: subtitles (text)
stream 3: audio 5.1 channels
stream 4: subtitles (text)
input file 'C.mkv'
stream 0: video 1280x720
stream 1: audio 2 channels
stream 2: subtitles (image)
読者ノート
このような情報はファイルに対して ffprobe を実行すれば確認できる。 ffprobe ノート 参照。
自動ストリーム選択の例¶
bash$ ffmpeg -i A.avi -i B.mp4 \
out1.mkv \
out2.wav \
-map 1:a -c:a copy out3.mov
出力ファイルは三つ指定されている。最初の二つ out1.mkv
, out2.wav
はオプション -map
がないので、これらのファイルストリームを ffmpeg
自身が決定する。
出力ファイル out1.mkv
はコンテナーファイルで、映像、音声、字幕のストリームを受け付ける。よって、ffmpeg は各種別の一つを選択しようとする:
映像:すべての入力映像ストリームの中で最も解像度の高いものは
B.mp4
のストリーム 0 であるので、それを選択する。音声:チャンネル数が最も多い
B.mp4
からストリーム 3 を選択する。字幕:
A.avi
とB.mp4
のうち最初の字幕ストリームであるB.mp4
からストリーム 2 を選択する。
出力ファイル out2.wav
は音声ストリームのみを受け付ける形式なので、B.mp4
のストリーム 3 のみが選択される。
出力ファイル out3.mov
ではオプション -map
が設定されているため、ストリーム自動選択は行われない。オプション -map 1:a
により、二番目(序数はゼロから)の入力である B.mp4
から音声ストリームのすべてがオプション
-c:a copy
指定により選択される。この出力ファイルは、これ以外のどのストリームも含まない。
最初の二つの出力では、含まれるストリームのすべてが transcode される。選択される encoder は各出力フォーマットで登録された既定のもので、選択された入力ストリームの codec と一致しない場合がある。
三番目の出力では、音声ストリームの -codec
オプションが copy
に設定されているため、decoding - filtering - encoding の処理は発生しないはずだが、その可能性はある。選択ストリームパケットは入力ファイルから伝達されて、出力ファイル内で混合されなければならない。
自動字幕選択の例¶
bash$ ffmpeg -i C.mkv out1.mkv -c:s dvdsub -an out2.mkv
out1.mkv
は字幕ストリームも受け入れる Matroska コンテナーファイルだが、映像と音声のストリームのみが選択されなければならないものとする。C.mkv
の字幕ストリームは画像ベースであり、Matroska muxer の既定の字幕 encoder はテキストベースなので、字幕の transcode 操作は失敗すると予想され、したがってストリームは選択されないのだ。
読者ノート
ここでは字幕コピーオプションを明示しないと失敗する挙動を逆用しているということ。
-c:s dvdsub -an out2.mkv
一方、out2.mkv
ではコマンドに字幕 encoder が指定されている (-c:s
dvdsub
) ので、映像ストリームに加えて、字幕ストリームが選択される。音声ストリームは -an
を指定してあるので含まれない。
ラベルなしフィルターグラフ出力の例¶
bash$ ffmpeg -i A.avi -i C.mkv -i B.mp4 \
-filter_complex "overlay" \
out1.mp4 out2.srt
オプション -filter_complex
を使って、単一の映像フィルターからなるフィルターグラフを設定している。フィルター overlay
は、ちょうど二つの映像入力を必要とするものだが、何も指定されていないので、最初に利用可能な映像ストリーム二つ、
A.avi
と C.mkv
が選ばれる。
フィルターの出力パッドにはラベルがないので、最初の出力ファイル out1.mp4
に送られる。このため、映像ストリームの自動選択は飛ばされ、B.mp4
のストリームが選択されるはずだった。音声ストリームは、B.mp4
のストリーム 3 など、最も多くのチャンネルを持つストリームが自動選択される。しかし、MP4 形式には既定の字幕 encoder が登録されておらず、利用者が字幕 encoder を指定していないため、字幕ストリームは選択されない。
Todo
パッドが何を指すのかわからない。
2 番目の出力ファイルである out2.srt
は、文字ベースの字幕ストリームのみを受け付ける。したがって、最初に利用可能な字幕ストリームは C.mkv
にあるが、それは画像ベースであるため無視される(さっきと同じ理由)。選択ストリームである B.mp4
のストリーム 2 は、最初の文字ベースの字幕ストリームだ。
ラベルありフィルターグラフ出力の例¶
bash$ ffmpeg -i A.avi -i B.mp4 -i C.mkv \
-filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
-map '[outv]' -an out1.mp4 \
out2.mkv \
-map '[outv]' -map 1:a:0 out3.mkv
ラベル [outv]
の付いた出力パッドが二度 -map
されているので、上記のコマンドは失敗する。どの出力ファイルも処理されない。
bash$ ffmpeg -i A.avi -i B.mp4 -i C.mkv \
-filter_complex "[1:v]hue=s=0[outv];overlay;aresample" \
-an out1.mp4 \
out2.mkv \
-map 1:a:0 out3.mkv
上記のコマンドも失敗する。フィルター hue
の出力は [outv]
というラベルがありながら、どこにも -map
されていない。次が修正済みコマンドだ:
bash$ ffmpeg -i A.avi -i B.mp4 -i C.mkv \
-filter_complex "[1:v]hue=s=0,split=2[outv1][outv2];overlay;aresample" \
-map '[outv1]' -an out1.mp4 \
out2.mkv \
-map '[outv2]' -map 1:a:0 out3.mkv
[1:v]
指定により、B.mp4
の映像ストリームはフィルター hue
に送られ、その出力はフィルター split
でいったん複製され、双方の出力にラベルが付けられる。そして、複製のそれぞれが出力ファイル out1.mp4
, out3.mkv
それぞれに -map
される。
フィルター overlay
は、映像入力を二つ必要とし、最初の未使用の映像ストリーム二つを使用する。ここでは A.avi
と C.mkv
のストリームだ。overlay
出力はラベルが付いていないので、オプション -map
の有無にかかわらず、最初の出力ファイル out1.mp4
に送信される。
フィルター aresample
は、最初の未使用の音声ストリーム A.avi
を送信する。このフィルター出力もラベルが付いていないため、最初の出力ファイルに
-map
される。オプション -an
は音声ストリームの自動的または手動的なストリーム選択を無効にするだけで、フィルターグラフから送信される出力は抑制しない。マップされたストリームはどちらも out1.mp4
の -map
されたストリームの前に並べられる(この説明の最後に注意)。
out2.mkv
に -map
される映像、音声、字幕ストリームは(出力オプションが何もないので)すべて自動選択によって決定する。
out3.mkv
はフィルター hue
から出力された複製映像 ([outv2]
) と、
B.mp4
の最初の音声ストリーム (-map 1:a:0
) から構成されている。
問題:では out2.mkv
の映像ストリームの具体的な内容は?