azukipochette's weblog

memory dump (mini)

ffmpeg で動画を変換して 1TB 節約した話

f:id:AzukiPochette:20200224052326j:plain unsplash-logoCeline Nadon

はじめに

私は 2018 年に発売された mac mini を遊び用途で使っています。

写真や動画の保存にも使っているのですが、画像や動画は "容量食い" が激しいため、保存場所を考え直すことに *1。写真のほとんどは、Cloud Storage や Photo Services に移動させましたが、動画だけは良い移行先を見つけられず、オフラインで保存することにしました。

そこで、USB 3.0 で接続できる 4TB の大容量 HDD を購入し、mac mini に接続して使っていました。が... 約半年、あっという間に容量が枯渇しました。(「大容量だから」と気にせず保存していたのが主敗因です...)

HDD の追加購入も考えましたが、そもそも 4TB の HDD を半年ぐらいで使い方したことを考えると、この先、家の片隅に HDD が山積みされて、消費電力が心配になる未来が見えたため、追加購入を考える前に、さすがに何か "工夫" をしようと考えます。

対策: ffmpeg で動画を変換しよう

容量削減の方法として、動画の解像度を下げて変換することにしました。

これまでは、視聴時には 4K モニターを使うため、なんとなく 4K に耐えうる高画質で保存していましたが、それだと 1 ファイルあたり 6 - 7 GB あるため、解像度を下げて 2 GB 以下を目指します。

100 ファイル以上あるファイルを変換することになるため、スクリプトで扱うことができる(コマンドラインで実行できる)、"ffmpeg" を選択することにしました。

ffmpeg を mac に導入する方法は、Homebrew を使うと非常に簡単です。Homebrew は、mac OS 向けのパッケージ マネージャーで macOS の開発者界隈では広く使われています。インストール方法などについてはこの記事で詳しく扱いませんが、下記を見れば簡単に導入できます。

brew.sh

Homebrew で ffmpeg をインストールするには、以下のコマンドを実行します。

brew install ffmpeg

画質の考察

動画を変換する上で最も重要なことは「どういった画質として保存するか」であることは言うまでもありません。基本的に、画質が良ければ自然と大きくなり、小さくしようとすると画質は悪くなるものです。

元の動画の画質を知るには ffprobe コマンドを使用します。 ffprobe も ffmpeg も単純に実行すると、バナーと呼ばれる情報が表示されてしまうので、以下のように -hide_banner オプションを付与することをオススメします。

ffprobe -hide_banner /path/to/sample.avi

残念ながら「良い感じ最適化」といった曖昧な指定はできないため、動画素人な私はどの画質にするのかを一番悩みました。

結局、世界最大の動画サイトである YouTube の解像度やビットレートを参考にすることにしました。

support.google.com

簡単に表にまとめると、以下のようになります (30fps の場合)。

名称 横(px) 縦(px) 動画ビットレート範囲
4K (2160p) 3840 2160 13,000 ~ 34,000 Kbps
2K (1440p) 2560 1440 6,000 ~ 13,000 Kbps
FHD (1080p) 1280 720 3,000 ~ 6,000 Kbps
HD (720p) 1280 720 1,500 ~ 4,000 Kbps
(480p) 720 1280 500 ~ 2,000 Kbps
(360p) 640 360 400 ~ 1,000 Kbps

補足 : 480p は日本の DVD ビデオの製品規格で使われる解像度です。

上記を参考にいろいろと実験したところ、HD 画質まで落とせば、目標値である 2GB 程度まで下げられることがわかりました。

※ 当然ですが、どれが最適な品質というのは人それぞれだと思いますので、ご自身で数ファイル実験してみてください。

コーデックを選ぶ

次に、コーデックを選ぶことなりますが、対応しているコーデックは環境によって異なります。 サポートしているコーデックの一覧を取得するには以下のコマンドを実行します。

ffmpeg -hide_banner -codecs

実行すると、対応しているコーデックの一覧が表示されます。

近年、代表的な動画のフォーマットといえば "H.264"、"H.265/HEVC" ではないかと思います。

H.265 は、その名が示すとおり H.264 の後継規格で、60 fps をサポートしており、同画質を半分程度のサイズで実現するという今回の期待にぴったりのフォーマットです。H.264 と H.265 の違いはインターネット上に大量に記事がありますので、そちらを参考にしてください。

個人的には、富士通研究所の以下の記事がわかりやすかったです。

www.fujitsu.com

しかし、実験してみると変換が非常に遅く、時間がかかるため今回は "H.264" を選択することにしました (私の環境では、約 10 倍ほど H.264 のほうが高速でした)。*2

H.264 に決めたら、コーデック一覧から H.264 を探します。私の環境では以下のように表示されています。

 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (encoders: libx264 libx264rgb h264_videotoolbox )

見ると、"encoders" のところに 3 つ表示されていることがわかります。簡単に解説すると、以下の通りです。

エンコーダー 概要
libx264 オープンソースのソフトウェア エンコーダー
libx264rgb YUV の代わりに RGB を出力する libx264 のパッチ バージョン。
h264_videotoolbox Apple が提供している H.264 向けのハードウェア エンコーダー

(おまけ) libx264 で mac mini アツアツ事件

当初、"libx264" で実験していただのですが、エンコードを開始する度に mac mini からけたたましい音が聞こえ、本体の天板を触ってみると非常に熱い。おもむろにアクテビティ モニタを開くと、CPU 負荷が最大に。しかも、%GPU は 0% のまま。「...もしかして、ハードウェア エンコードに対応してないの!?」と思って調べてみると、mac mini (2018) には T2 チップが搭載されており、h264_videotoolbox を使うことでハードウェア エンコードができることを知りました。

ffmpeg を実行する

画質とフォーマット/エンコーダーを決めたら、いよいよ ffmpeg で動画を変換します。

私の場合は、HD (1280x720) サイズで、ビットレートは 1,500Kbps、h264_videotoolbox を使ってエンコードをしたいので、以下のように実行しました。

ffmpeg -i SAMPLE.mp4 -s 1280x720 -c:v h264_videotoolbox -b:v 1500k SAMPLE_HD.mp4

変換を自動化する

手作業ですべてのファイルを変換するのは大変なので、スクリプトを書いて自動化します。私の場合は、PowerShell (pwsh) を使ってスクリプトを書きました。

下記のスクリプトでは、現在のフォルダー配下のファイルを再帰的に取得し、ffmpeg で変換、変換に成功したら元ファイルを消しています。 ファイル名は HD で変換したことがわかるように、"_HD" を付与するようにしています。

$files = Get-ChildItem -Recurse -File
foreach ($file in $files)
{
    $newFullName = Join-Path -Path $item.DirectoryName -ChildPath ($item.BaseName + "_HD" + $item.Extension)
    $process = Start-Process -FilePath ffmpeg -ArgumentList "-i $($item.FullName) -s 1280x720 -c:v h264_videotoolbox -b:v 1500k $newFullName" -Wait
    if ($process.ExitCode -ne 0) {
        Remove-Item $item
    }
}

(おまけ) タグを使って除外する

全部のファイルを変換する場合は上記でいいのですが、一部の大切なファイルだけは除外したいという状況になりました。簡単にやるならば、フォルダー名やファイル名に除外を示す文字 (-,_,@ など) を付けてもいいのですが、名前を変えると名前順に並ばないなどの問題があるため、タグを使って除外することにしました。

finder のタグは拡張属性として保持されており、次のコマンドで取得できます。

xattr /path/to/yourfolder

タグが付与されている場合は、以下のような拡張属性が表示されます。

com.apple.FinderInfo
com.apple.metadata:_kMDItemUserTags

タグの情報は "com.apple.FinderInfo" に入っています。取得例を以下に示します。

☁  ~  xattr -p -x com.apple.FinderInfo /path/to/yourfolder
00 00 00 00 00 00 00 00 00 0C 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

10 バイト目に "0x0C" という値が見えますが、これが赤色をタグを示しています。

実験した結果を以下に示します。

灰 (GREY) 0x02
緑 (GREEN) 0x04
紫 (PURPLE) 0x06
青 (BLUE) 0x08
黄 (YELLOW) 0x0A
赤 (RED) 0x0C
橙 (ORANGE) 0x0E

実験の結果、タグを消すと拡張属性自体が消えるので、中身は見ずに拡張属性があるかどうかで判断することにしました。拡張属性は、タグ以外でも様々なツール (Dropbox etc) で利用されているため、ご自身の環境で調べてみてください。

残念ながら、PowerShell では拡張属性は読み取れないため、xattr の出力結果を使ってタグがあれば除外することにします。

コード例を以下に示します。

$dirs = Get-ChildItem -Directory
foreach ($dir in $dirs) {
    $attr =  xattr $dir.FullName
    if ($attr -eq $null) {
        # タグなし
    } else {
        # タグあり
    }
}

それでは、よい ffmpeg 生活を。Enjoy!

*1:mac mini に搭載されている SSD は 256GB です

*2:当初、最近話題の AV1 (AOMedia Video 1) で変換しようかと思っていましたが、見事に QuickTime が対応しておらず、メジャーなフォーマットを選ぶことにしました。