ffmpeg ノート

ffmpeg のマニュアルを読み取る。コマンドパイプラインの構造、フィルターグラフ、ストリーム選択の仕組みについて述べる。また、ここで述べる事実には ffplay と共通するものがある。

コマンドの構造

ffmpeg のコマンドラインの書式は次のように定められている:

ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...

要点はこういう感じだろうか:

  • 全体オプション、入力、出力の順番に指定する必要がある。

  • 入力も出力も複数指定することが可能だ。

  • 入力も出力もファイルシステムのパスでもインターネット上の URL でもいい。

  • 入力ファイルそれぞれの直前に -i を明示する必要がある。一方、出力ファイルにはそのようなものがない。

コマンド処理のパイプラインは次のグラフのように表される:

flowchart LR input[input] epackets[encoded\ndata\npackets] frames[frames] dpackets[decoded\ndata\npackets] output[output] input --demuxer--> epackets epackets --decoder--> frames --encoder--> dpackets dpackets --muxer--> output
  1. ffmpeg は libavformat ライブラリーを呼び出して入力ファイルを読み、それらからエンコードされたデータを含むパケットを得る。入力ファイルが複数ある場合もある。

  2. エンコード済みパケットは、decoder に渡される。非圧縮フレームを生成し、フィルターによってさらに処理することができる。

  3. フィルターの後、フレームは encoder に渡され、それらをエンコードし、エンコード済みパケットを出力する。

  4. 最後に、エンコード済みパケットは muxer に渡され、出力ファイルに書き出される。

フィルターグラフ

エンコードする前に libavfilter ライブラリーのフィルターを使って生の音声・映像フレームを処理することができる。フィルターの連鎖がフィルターグラフを形成する。ffmpeg はフィルターグラフを simple と complex の二種類に分類して扱う。利用者の立場からするとコマンドラインの書き方が変わってくる。後者のほうが文字通り複雑になる。

単純フィルターグラフ

入力と出力がそれぞれ一つずつあり、両方とも同じ型であるフィルターグラフを単純フィルターグラフ (simple graph) という。最初の図における decode と encode の間に段階を追加するだけで、それを表現することができる。

flowchart LR frames --simple filtergraph --> filtered\nframes -- encoder --> encoded\ndata\npackets

単純フィルターグラフは、ストリームごとにオプション -filter で設定する。次のような別名もあるので覚えておく:

  • -filter:v, -vf

  • -filter:a, -af

例えば次のようなコマンドでは、オプション -vf yadif,scale=256:224 部分が単純フィルターグラフを構成している:

bash$ ffmpeg -i input.mp4 -vf yadif,scale=256:224 output.mp4

ここでは映像フィルター yadifscale を順番に適用している。カンマ , でフィルターを区切って列挙するだけでいい。各フィルターには固有の引数を与えることもある(この例では scale でそうしている)。どのフィルターオプションにおいても次の書式になると思っていい:

filtername=param1=arg1:param2=arg2:...
filtername=arg1:arg2:...

このコマンドを図式化したものを記す:

flowchart LR input --> yadif --> scale --> output

フレームのプロパティーを変更するフィルターもあるが、普通はフレームの内容には触れない。例えば、フィルター fps はフレーム数を変更するものの、その内容には触れない。もう一つの例はフィルター setpts だ。これはタイムスタンプを設定するだけで、それ以外はフレームを変更せずに通過させる。

複雑フィルターグラフ

入出力ストリームが複数あり得るものを複雑フィルターグラフ (complex filtergraph) という。模式的に書くと例えば:

flowchart LR input0 --> complex\nfilter\ngraph --> output0 input1 --> complex\nfilter\ngraph --> output1 input2 --> complex\nfilter\ngraph

複雑フィルターグラフはオプション -filter_complex で構成する。その性質上、単一ストリームやファイルに一義的に関連付けることができないため、このオプションは global だ。

オプション -lavfi-filter_complex と同じだ。

複雑なフィルターグラフの明らかな例はフィルター overlay だ。二つのビデオ入力と一つのビデオ出力を持ち、一方のビデオが他方のビデオの上に重なっているものだ。その音声版に相当するのがフィルター amix だ。例については別途述べる。

ストリーム copy

ストリームコピーとはオプション -codec: copy で機能するモードだ。意味としては基本図式の decoder と encoder の段階を省き、demuxer と muxer のみを機能させるということだ。用途としては、コンテナー形式を変換したり、コンテナーレベルのメタデータを変更したりするものだ。この状況での ffmpeg コマンドパイプラインの基本図式は次のように単純になる:

flowchart LR input -- demuxer --> encoded\ndata\npackets --muxer--> output

符号化や復号化処理がないため、たいへん高速で品質劣化もない。フィルターを適用することは当然不可能だ(フィルターは非圧縮データに対して機能するものだ)。

ストリーム選択

オプション -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.aviB.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.aviC.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.aviC.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 の映像ストリームの具体的な内容は?