ffmpegで24fps化してみる(pullupとfieldmatch)

ffmpeg 24p pullup and fieldmatch filter
ffmpegを使って24fps化をするコマンドを忘れないうちに書いておきます。24fps化は逆テレシネやインターレース解除など面倒な事が起こります。fieldmatchとpullupフィルタを試します。
今回は急に思い立って2日ほどで試したコマンドなので最適かどうかはわかりません。


ビデオファイルの再エンコード方法を検討し直しています。
handbrakeでmatroskaコンテナにうまく入らないファイルがあったので、今回はffmpegを使って再エンコードする方法を検討しました。

どのように再エンコードするか?
今回はアーカイブ用に、解像度を1280x720ドットに、フレームレートを24fpsプログレッシブにしてみます。音声は再エンコードしません。映像のみ再エンコードします。また、チャプターや字幕があれば維持してみます。

ffmpegで24fps化する

映像トラック一つ、複数の音声トラック、字幕やチャプタートラックがあるビデオファイルを映像のみ再エンコードする事にします。

入力ファイル名をinput.mkvとします。出力ファイル名をoutput.mkvとします。
ファイルフォーマットは私としてはmatroskaをお薦めします。なんでも入れられる便利なコンテナです。iPhoneでしか再生しないという方はmp4でもいいと思いますがコンテナとしてはいまいちです。iPhoneに拘らなければ今どきmp4にする理由はないでしょう。昔にDVDをチャプタと字幕付けて作ったのに…という方もmkvならそのまま入りますよ。

早速ですがffmpegのコマンドを紹介します。
2種類の例をあげてみました。基本的なコマンドは同じですが、逆テレシネとインターレース解除を行うフィルターが異なります。
ffmpeg.exe  -i input.mkv -ignore_unknown  -map 0 -c copy -c:v libx264 -filter_complex "dejudder,fps=30000/1001,fieldmatch,decimate,scale=1280:720,fps=24000/1001" -g 48 -crf 23 -preset medium  output.mkv
ffmpeg.exe  -i input.mkv -ignore_unknown  -map 0 -c copy -c:v libx264 -filter_complex "pullup,dejudder,scale=1280:720,fps=24000/1001" -g 48 -crf 23 -tune animation -preset medium output.mkv 

コマンドの内容を説明していきましょう。

"-i input.mkv"

-i は入力を示すオプションです。input.mkvファイルを入力ファイルにしています。今回はmkvファイルを例にしていますが、mp4,tsやmpegなどももちろん入力可能です。

"-map 0 -c copy"

入力ファイルにあるトラックをすべてコピーします。ビデオ、オーディオ、字幕、チャプター、メタデータ等々です。

"-ignore_unknown"

全てのトラックをコピーと言ってもffmpegが認識できない形式のトラックもあるでしょう。するとエラーで終了してしまいます。このオプションを指定すると認識できないトラックを無視します。そのままコピーする場合は"-copy_unknown"とします。

"-c:v libx264"

全てのトラックをコピーと指定しましたが映像のトラックだけはlibx264でエンコードするよう指定しています。もちろんh.265などお好きなエンコーダを指定できます。

fieldmatchを使う場合

今回の肝となるフィルターオプションの指定です。まずはfieldmatchを使う例です。

' -filter_complex "dejudder,fps=30000/1001,fieldmatch,yadif=0:-1:1,decimate,fps=24000/1001'

fieldmatchフィルタが主役です。
9.52 fieldmatchField matching filter for inverse telecine. It is meant to reconstruct the progressive frames from a telecined stream.
-中略-
The decimate filter currently only works for constant frame rate input. If your input has mixed telecined (30fps) and progressive content with a lower framerate like 24fps use the following filterchain to produce the necessary cfr stream: dejudder,fps=30000/1001,fieldmatch,decimate.
逆テレシネ処理をしますが重複フレームの削除は行わないので、この後にdecimateフィルタを呼べと書いてあります。
最初の"dejudder"フィルタでは一部だけテレシネになっているビデオのジャダー(ぎくしゃくした動き)を除去します。このフィルタの出力は可変フレームレートになります。
decimateフィルタは固定フレームレートでしか動かないのでfieldmatchの前に"fps=30000/1001"で29.976fpsへ変換します。入力ファイルのフレームレートに関係なく30000/1001とする点に注意します。
fieldmatchフィルタは縞を検知するとインターレースマークを付加するようなので、この後にyadifフィルタを置いています。

"scale=1280:720"
ビデオのサイズを1280x720ドットに変換します。

"fps=24000/1001"
フレームレートを23.976にします。

ソースによってはyadifフィルタはいりませんが、ソース毎にフィルターを分けるのも面倒なので入れっぱなしにしています。

pullupを使う場合

-filter_complex "pullup,dejudder,scale=1280:720,fps=24000/1001"

"pullup"フィルタで逆テレシネ処理を行います。"dejudder"フィルターでジャダーを減らします。

"scale=1280:720"
ビデオのサイズを1280x720ドットに変換します。

"fps=24000/1001"
フレームレートを23.976にします。

"-g 48"

GOP長を48にします。
再生時にシークバーでスキップしようと思ったら指定した場所から始まらず困った事はありませんか?GOP長の間隔でしかシークできません。
ストリーミングやアーカイブ用ならGOP長を数百の値にすると圧縮率が上がります。

"-crf 23"

固定品質でエンコード品質を指定しています。
値は18から28くらいで調整します。値が小さいほど高品質ですがファイルサイズが大きくなります。私は圧縮を優先して23ほどを使いますが、お薦めの数値は明示できません。値を変えて自分なりの値を決めます。

"-preset medium"

x264のプリセットを指定しています。mediumはデフォルト値なので指定しなくても良いのですが比較的指定する事が多いオプションなので書いておきました。

ヘッダの壊れたAACストリームトラックがある場合

"-bsf:a aac_adtstoasc"

ストリーミングされたaacファイルなどでは開始パケットが途中で途切れて正しいヘッダ情報が無い場合があります。これを修復するのにこのビットストリームフィルターを指定します。aacトラックだけがある場合に使えます。ac3など他の形式のトラックがあるとエラーになります。その場合はaacトラックのIDを指定する必要があるでしょう。
mp4だと自動的に指定されるようです。matroskaは指定しておくと良いでしょう。

あとがき

今まで再エンコードにはhandbrakeを使っていました。しかし、フィルターの少なさから対応しきれないビデオファイルもちらほら。
また、matroskaコンテナに不完全なストリーム形式のaacファイルを入れると再生できないファイルが出来上がるようです。これはffmpegでも同じようになりますが、aac_adtstoascビットストリームフィルタを指定して回避できました。これでもダメなファイルがありましたけど。

pullupとfieldmatchを比べるとpullupの方が処理が速いようです。私の気のせいかfieldmatchの方が輪郭がきれいに見えます。動きの滑らかさはfieldmatchの方が失敗が少ないです。
今回の設定でインターレース解除がきちんと動くか、動きが滑らかかの検証はまだ不十分です。24fps化は動きが滑らかにならない事もあってやりたくはないですね。ですがもう圧縮優先です!

yadifはスライス処理に対応しています。pullup,fieldmatchとdejudderフィルターはスライスに対応していません。マルチコアの恩恵を受けられず処理速度のボトルネックになってしまいます。

x264を使う場合はcrfで画質を指定する事を強くお勧めします。
ビットレート指定はDVDなど円盤に焼いていた頃の名残です。
もう一つDVD時代の名残で勘違いしやすい点に可変ビットレートがあります。固定品質は映像の複雑さでビットレートが変わります。ですが、可変ビットレートとは全く異なります。可変ビットレートはDVDプレーヤーなどわずかなメモリーしか持っていない機器で、ほんの数秒間でビットレートを分配します。数秒間ずっと動きが激しいシーンが続いたら可変ビットレートなど意味はありません。

crfの値を決めるのが最大の難問です。
これを考え始めると、テレビ用とスマートフォン用とでは最適なcrf値が異なるという事に気づいてしまいます。ディスプレイの表示密度を考慮する必要があります。5インチのスマートフォンから50インチのテレビになればそれだけ拡大して見ているのと同じです。5インチの画面では気づかない粗も見えてしまいます。
さぁ、ディスプレイの画素密度別にエンコードを始めましょう!

同じサイズのディスプレイしか使わないとしても、ビデオの解像度でcrf値を変えた方がよいです。低解像度のビデオはドットが拡大表示される事になるのでより小さいcrf値を使います。
面倒なので適当な一つの値にしちゃいますけどね。

画質の指標でSSIM値というのがあります。8x8ドットの部分画像が元とどれだけ一致しているかを表します。密度の情報を含まないのであまり意味のない指標です。
5インチのスマホ画面ならSSIM値が悪くても気づかないでしょう。

このため、crf値はこれだ! なんて言う事ができません。頼りになる指標もなく困ってしまいますよね。ですが、どんな値だろうとあなたが満足すればそれで良いのです。
えっ⁉元のファイルよりサイズが大きくなったって?

おまけ

今回の処理の流れでデコードにハードウエアを使ってみます。cudaを使います。エンコードはlibx264のままです。
使うフィルタがハードウエア処理できないためCPUとcudaとのメモリー転送が増えるだけで意味がないと思いますが、ハードウエアを使う練習です。入力ファイルはh.264であることが前提です。
ffmpeg.exe  -hwaccel_device 0 -hwaccel cuvid -c:v h264_cuvid -i test.mkv -ignore_unknown  -map 0 -c copy -bsf:a aac_adtstoasc -c:v libx264 -filter_complex "hwdownload,format=nv12,dejudder,fps=30000/1001,fieldmatch,yadif=0:-1:1,decimate,fps=24000/1001,scale=1280:720" -g 48 -crf 23 -preset medium -y out.mkv
これで動きました。ほんとかな?
ハードウエアデコードしたのでcudaのメモリーに映像イメージが出来上がると思われます。フィルター処理はCPUで行うため、最初にCPUへイメージを転送する必要があるようです。その後はCPU上の処理なので今までと同じです。
ん?ビットストリームフィルタを書く場所が気になってしまう。

コメント

最近のコメント

Threaded Recent Comments will be here.