特定のS3バケットへ定期的にアップロードされる画像を元にして、タイムラプス動画(要はスライドショー)が作りたくて試してみた。
https://github.com/sparkgene/lambda_image_to_video
Lambda functionで動画編集するには
AWS LambdaにはImageMagickは入ってるけど、動画を編集したい場合はffmpegが必要になってきます。
Pythonの場合MoviePyという素晴らしいpython moduleがあり、これは実行時にffmpegをダウンロードしてくれるので、Mac上で試す場合でもffmpegを入れてなくても実行時に環境を整えてくれる。(ただし、依存するライブラリは事前に入れておく必要があるけど)
MoviePyの自動ダウンロード
先に書いたようにMoviePyが呼ばれた時に自動でダウンロードてくれるのだが、実際にやってみると正しく動いてくれない。
ソースを追ってみると、ダウンロード先がlambda functionが権限を持たないディレクトリであるために、失敗する事がわかった。
https://github.com/imageio/imageio/blob/master/imageio/core/util.py#L408-L410
userDir = '/var/tmp'
この部分を
userDir = '/tmp'
に書き換えてあげれば、ダウンロードは正しく行われるが、毎回同じ環境で動くわけではないので、最悪の場合lambda functionが実行されるたびにダウンロードが行われてしまうので、現実的ではない。
zipに含める
ffmpegのバイナリサイズがそれなりなので、zipファイルがそれなりのサイズとなってしまうが、この方法が無駄がないと思われる。ただし、実際に環境を整えようとしたら、だいぶ苦労した。
moviepyがnumpyを利用しているため、numpy関連の静的ファイルも必要になってきます。
ビルド方法やビルド済みのライブラリがこちらで公開されているので、今回はそれを利用します。
https://github.com/vitolimandibhrata/aws-lambda-numpy
また、ffmpegはこちらで公開されている各環境向けのバイナリを利用します。
https://github.com/imageio/imageio-binaries
zipに含める場合でも、Lambdaの場合は配置されるディレクトリでは実行権限がつけられないので、一旦/tmp
に移動してから実行権限をつけて、MoviePyが参照するffmpegのバイナリを示す環境変数に移動したファイルのパスを指定してあげる必要がある。
https://github.com/sparkgene/lambda_image_to_video/blob/master/lambda_function.py#L23-L26
ffmpegが使えるPythonのLambda Functionを作成する
AWS Lambdaと同じ環境でビルドする必要があるので、Amazon LinuxのEC2インスタンスを用意します。
最新の状態にする
sudo yum -y update
sudo yum install -y git
sudo yum -y install gcc-c++
リポジトリをcloneする
git clone https://github.com/sparkgene/lambda_image_to_video.git
ビルド済みのnumpyのリポジトリをcloneする
git clone https://github.com/vitolimandibhrata/aws-lambda-numpy.git
ライブラリとnumpyをコピー
cp -R aws-lambda-numpy/lib lambda_image_to_video/
cp -R aws-lambda-numpy/numpy lambda_image_to_video/
freeimageのバイナリをダウンロード
wget https://github.com/imageio/imageio-binaries/raw/master/freeimage/libfreeimage-3.16.0-linux64.so -O lambda_image_to_video/lib/libfreeimage.so
ffmpegのバイナリをダウンロード
wget https://github.com/imageio/imageio-binaries/raw/master/ffmpeg/ffmpeg.linux64 -O lambda_image_to_video/ffmpeg.linux64
lambda functionの修正
cd lambda_image_to_video
vi lambda_function.py
image_bucket = "" # 変換元の画像ファイルが格納されているバケット
video_bucket = "" # 変換後の動画を含めるバケット
このラムダファンクションはimage_bucket
にyyyy/mm/dd/hoge-0001.jpg
のようなキーで画像が保存されていることを想定しています。(0001.jpgの部分は連番ではなくてもいいですが、ソートしてから動画を作成するので、ソート時に正しい順番に並ぶようにネーミングしてください)
動画はvideo_bucket
にyyyy/mm/yyyymmdd.mp4
というキーで出力します。
動画は1fpsで生成されるので、利用する画像数=動画の秒数となります。
moviepyのインストール
pip install -r requirements.txt -t ./
zipファイルに固める
zip -r func.zip . -x *.git*
Lambda FunctionのRole
Roleは以下のポリシーを持つRoleを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Sid": "Stmt1234567890",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectAcl",
"s3:ListBucket",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::image-input",
"arn:aws:s3:::image-input/*",
"arn:aws:s3:::video-output",
"arn:aws:s3:::video-output/*"
]
}
]
}
動画を生成するのでメモリはある程度余裕をもたせた設定にするのが良さそう。
画像20枚ぐらいで動画を作った時は以下のように、200M近くメモリを消費しました。また、処理時間も画像の枚数に比例するので、メモリとタイムアウトの設定は実際の利用状況をみて設定するのが良さそうです。
REPORT RequestId: 1c8d6fcf-21d4b17b5ec2 Duration: 33316.13 ms Billed Duration: 33400 ms Memory Size: 256 MB Max Memory Used: 195 MB
参考にしたサイト