iOSアプリにtanersener/ffmpeg-kitを組み込んで、iPhoneで録画した動画をffmpegを使ってh264エンコードするとどの動画にもノイズが入る問題が発生した。macOS上で同じ動画をエンコードした場合にはノイズが乗らない。なぜノイズの有無が発生してしまうのか? この差異を認識するのに随分時間をかけてしまった。
結論としてはffmpegで使われるエンコーダーが異なるのが原因である。iOSアプリ上ではApple社が実装したコーデックの h264_videotoolbox
が使われ、macOS上では libx264
が使われる。h264_videotoolboxはハードウェアエンコードをおこなうのでエンコード時間は短くて済むが画質が悪くなってしまう。
h264
といったエイリアスを使うのではなく、厳密な名称である h264_videotoolbox
と libx264
を指定すれば、macOS上でも同等の現象を再現できる。
- 画質が悪いことに気づいた経緯
- -c:v h264 はエイリアスであり内部的には異なるコーデックが使われていた
- h264_videotoolbox を使って高画質動画にするためにはビットレートを上げる
- 結論
画質が悪いことに気づいた経緯
以下のパラメータを使って動画のエンコードをした。テスト動画を用いて複数のパラメータを使ってエンコードして、サイズと画質のバランスが良いパラメータを選んだ。
ffmpeg -i original.mov -c:v h264 -b:v 1000k output_h264.mp4
しかしiOS上で動画をエンコードすると下図のようにブロックノイズが乗ってしまう。期待する画質が得られないことがわかった。
当初は同じh264エンコーダーを指定しているのに何故画質に違いが出るのかわからなかった。
-c:v h264
はエイリアスであり内部的には異なるコーデックが使われていた
ffmpegに詳しい方にとっては当たり前のことなのかもしれないが、-c:v h264
はエイリアスであり内部的には異なるコーデックが使われていた。これは数日調査してわかった。
homebrew経由でインストールした ffmpeg で利用可能なコーデックを調べると以下の結果が得られる。
$ffmpeg -codecs DEV.LS h264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (encoders: libx264 libx264rgb h264_videotoolbox )
iOSアプリ上で ffmpeg -codecs
を実行してみたところ以下の結果が得られた。iOSアプリ上では libx264
を使うことができない*1。
DEV.LS h264 (encoders: h264_videotoolbox )
つまり -c:v h264
はエイリアスであり、h264を指定するとmacOSでは libx264
のエンコーダーが使われ、iOSでは h264_videotoolbox
のエンコーダーが使われることがわかった。
エンコードパラメータとして h264
を使うのではなく、より厳密な h264_videotoolbox
や libx264
を指定していればもっと早く画質が悪い原因が特定できていたと思う。
h264_videotoolbox を使って高画質動画にするためにはビットレートを上げる
iOSでは h264_videotoolbox
を使わないといけないが、libx264
の感覚でビットレートを指定すると低画質になってしまう。h264_videotoolboxエンコーダーを使って高画質の動画にするにはどうすればよいか? 答えはStack Overflowに書かれていた。
- How to improve the output video quality with ffmpeg and h264_videotoolbox flag? - Stack Overflow
- macos - Optimally using hevc_videotoolbox and ffmpeg on OSX - Stack Overflow
SOのAnswerによると以下の通りである。ここでは libx265 と書かれているがこのまま libx264 と読み変えられる。
多くのハードウェアアクセラレーションエンコーダと同様に、hevc_videotoolbox は libx265 ほど効率的ではありません。そのため、libx265と同等の品質を実現するには、かなり高いビットレートを与えなければならないかもしれません。これは、H.264からHEVC/H.265に再エンコードする目的を失うかもしれません
h264_videotoolbox
を使う場合には libx264
より高いビットレートを指定しなければいけないことがわかった。どこまでビットレートを上げれば同等くらいの画質になるのか検証した。ここでの「同等くらいの画質」はさくさん基準によるため、各位には動画の性質によって適切な求めてもらいたい。そして良さげなビットレート設定を教えてもらえると嬉しい。
libx264(1000k) vs h264_videotoolbox(5000k)
h264_videotoolboxでの指定ビットレートを1000kから5000kに変更した。パラメータは下記のように指定している。
ffmpeg -i original.mov -c:v h264_videotoolbox -b:v 5000k output_videotool_5000k.mp4
h264_videotoolboxでもビットレートを -b:v 5000k
まであげることで、libx264の動画よりも高画質にすることができた。しかしlibx264の動画サイズは 17.7MB に対して、h264_videotoolboxの動画サイズは 78.9MB とかなり大きくなってしまった。
-b:v 1000k
指定した動画。右:h264_videotoolboxを使って-b:v 5000k
指定した動画。
libx264(1000k) vs h264_videotoolbox(2000k)
h264_videotoolboxでの指定ビットレートを1000kから2000kに変更した。パラメータは下記のように指定している。
ffmpeg -i original.mov -c:v h264_videotoolbox -b:v 2000k output_videotool_2000k.mp4
h264_videotoolboxの指定ビットレートを -b:v 2000k
にあげることで、libx264の動画と同等の画質にすることができた。libx264の動画サイズは 17.7MB に対して、h264_videotoolboxの動画サイズは 32.9MB と2倍程度に抑えることができている。
-b:v 1000k
指定した動画。右:h264_videotoolboxを使って-b:v 2000k
指定した動画。
結論
以上のことからffmpegで使われるエンコーダーが異なるのが原因であることがわかった。
iOSアプリ上ではApple社が実装したコーデックの h264_videotoolbox
が使われ、macOS上では libx264
が使われる。h264_videotoolboxはハードウェアエンコードをおこなうのでエンコード時間は短くて済むが画質が悪くなってしまう。macOSでも-c:v h264_videotoolbox
と指定すればハードウェアエンコーダーを使うことができる。
M1 MacBook Proを使っていてもlibx264
を使うと等倍でしかエンコードできない。これは10分の動画をエンコードするのに10分かかってしまうことを意味している。対して h264_videotoolbox
は6倍でエンコードできる。これは10分の動画を約1.6分でエンコードできることを意味している。
ユーザーを待たせることができるのであれば*2libx264
一択で良いだろう。画質面・ファイルサイズ面で優れている。もしユーザーを待たせることができないのであれば h264_videotoolbox
を使い、指定ビットレートを上げてファイルサイズを犠牲にして画質を上げる必要がある。