日別アーカイブ: 2017/04/20

ffmpegをGoogle Compute Engine経由で使ってみた

ffmpeg、便利ですよね。ただ、どうしてもエンコード作業という物はCPUパワーと時間を消費します。

背景

つい最近、Panasonic の4Kビデオカメラ VX980Mを購入しました。画質もよく、大変満足しているのですが、4K画質はとにかくファイルサイズが大きくて大変です。10分程度の動画でも約5GBになってしまい、普段の取り回しにはやや不便です。

そこで、もともとの4K動画は将来使用できるようにクラウド上へとバックアップし、ローカルには960×540 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などさらに負荷のかかる動画形式が一般的になってくれば、有用になるかもしれません。