ffmpeg、便利ですよね。ただ、どうしてもエンコード作業という物はCPUパワーと時間を消費します。
背景
つい最近、Panasonic の4Kビデオカメラを購入しました。画質もよく、大変満足しているのですが、4K画質はとにかくファイルサイズが大きくて大変です。10分程度の動画でも約5GBになってしまい、普段の取り回しにはやや不便です。
そこで、もともとの4K動画は将来使用できるようにクラウド上へとバックアップし、ローカルには960x540 30fpsにエンコードしたものを置いておくことにしました。
しかし、僕のメインマシンはMacBook Pro 12" (1.3 GHz Intel Core m7)なので、どう考えても本格的なエンコード作業には向いていません。
そこで、ffmpegをGoogle Compute Engineで動かし、クラウドでのエンコードを行ってみたので、引っかかった点も踏まえて、ここに報告させていただきます。
方法
まず、Google Compute Engineが使用できるようになっているのが大前提です。まだの人は導入を済ませてください。
まずは、インスタンスを作ります。今回はエンコード作業が目的ですので、CPUを強化した設定として、n1-highcpu-8
を使用します。また、ちょっとお高いですが、近い方が通信コストも少ないでしょうから、asia-northeast1-c
を選ぶことにしてみました。
% gcloud compute instances create cloudencode --image-family cos-stable --image-project cos-cloud --zone "asia-northeast1-c" --machine-type "n1-highcpu-8"
もちろん、UbuntuやCentOSを選んでも良いですが、Dockerを使った方が後々の取り回しも楽だと思いますので、Container-Optimized OSを選ぶことにします。
まずは、dockerでffmpegを動かすのを試してみましょう。イメージは人気のあるjrottenberg/ffmpeg
を選ぶことにします。
% gcloud compute ssh cloudencode --command="docker run -i --rm jrottenberg/ffmpeg" Unable to find image 'jrottenberg/ffmpeg:latest' locally latest: Pulling from jrottenberg/ffmpeg c62795f78da9: Pulling fs layer d4fceeeb758e: Pulling fs layer 5c9125a401ae: Pulling fs layer ... ... Status: Downloaded newer image for jrottenberg/ffmpeg:latest Hyper fast Audio and Video encoder usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}... ... ... ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.4) 20160609 ... ...
これでffmpegを動かすことができました。ここまではスムーズ。
と言うわけで、stdinとstdoutを用いてエンコード作業ができないかを試してみます。
% cat input.mp4 | gcloud compute ssh cloudencode --command="docker run -i --rm jrottenberg/ffmpeg -i pipe:0 -s 960x540 -r 30 -vsync passthrough -vcodec libx264 -acodec aac -ac 2 -ab 128k -threads 0 -f mp4 pipe:1" > output.mp4 ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers ... ... [mp4 @ 0x7f890685a600] muxer does not support non seekable output Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument Error initializing output stream 0:1 -- ...
どうやらだめなようです…。調べてみると、パイプを使った場合は、MPEG-PSでの出力はできないことが分かりました。ので、残念ながら、そのままmp4にはできないようです。
MPEG-TSならパイプへの出力にも対応しているので、こうすれば大丈夫です。
% cat input.mp4 | gcloud compute ssh cloudencode --command="docker run -i --rm jrottenberg/ffmpeg -i pipe:0 -s 960x540 -r 30 -vsync passthrough -vcodec libx264 -acodec aac -ac 2 -ab 128k -threads 0 -f mpegts pipe:1" > output.ts ffmpeg version 3.3 Copyright (c) 2000-2017 the FFmpeg developers ... ...
これでクラウドでの変換はうまいこといきました!
ただ、tsファイルのままだと、取り回しがまた面倒なので、こちらもローカルで再変換しましょう。
% ffmpeg -i output.ts -vcodec copy -acodec copy output.mp4
これで取り回しのしやすい小さいサイズのmp4ファイルのできあがり、です。
ただ、tsファイルをローカルにいったん保存するのは美しくない……。そう思うようであれば下記を試しましょう。zshが必要です。
% ffmpeg -i pipe:0 -vcodec copy -acodec copy output.mp4 2> /dev/null < <(gcloud compute ssh cloudencode --command="docker run -i --rm jrottenberg/ffmpeg -i pipe:0 -s 960x540 -r 29.970030 -vsync passthrough -vcodec libx264 -acodec aac -ac 2 -ab 128k -threads 0 -f mpegts pipe:1" < <(cat input.mp4 ) )
環境とバージョンによっては、inの方もMPEG TSである必要があるようです。その場合は残念ながら、さらに入れ子にして、
% ffmpeg -i pipe:0 -vcodec copy -acodec copy output.mp4 2> /dev/null < <(gcloud compute ssh cloudencode --command="docker run -i --rm jrottenberg/ffmpeg -i pipe:0 -s 960x540 -r 29.970030 -vsync passthrough -vcodec libx264 -acodec aac -ac 2 -ab 128k -threads 0 -f mpegts pipe:1" < <(ffmpeg -i input.mp4 -vcodec copy -acodec copy -f mpegts pipe:1 2> /dev/null ) )
これで、input.mp4 -> TS -> cloudのffmpeg -> TS -> output.mp4と変換することができます。
結果
肝心の性能です。
まずはlocalで実行している場合。
うーん、0.547x, 16 fpsと出ています。10分の動画の変換に約20分かかることになります。
それではクラウドではどうでしょう。
リアルタイム変換より速い速度で変換できています。4K動画の変換なので、Uploadが速度のボトルネックになる場合もあるかもしれませんが、ローカルの2.3倍の速度でエンコードできたようです。
注意点
Google Cloud Computingなので、料金がかかります。また、リソースの割当量にも注意が必要です。
MacBookやその他のNetBookなどでも、この方法なら動画のエンコードを行うことができます。ただ、巨大なファイルをアップロードする必要もあり、思ったより速度がかせげない、というのが正直な感想です。今後H.245/HEVCなどさらに負荷のかかる動画形式が一般的になってくれば、有用になるかもしれません。