ffmpeg-aws-lambda-layerでffmpegやffprobeを使う

## はじめに
動画のフォーマットやサイズ変更、サムネイルの生成に便利な ffmpegの実行環境を AWS Lambda関数とレイヤーをつかって構築する手順を記載します。
ここでは、[ffmpeg-aws-lambda-layer](https://github.com/serverlesspub/ffmpeg-aws-lambda-layer)を使用します。Lambda関数とは別にffmpegやffprobeを含む[AWS Lambda レイヤー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html)を作成することで、これらのコマンドがLambda関数実行環境の /opt 以下に呼び出されます。デプロイパッケージにコマンド実行用のバイナリを含めることなくライブラリのように呼び出すことができます。

[Amazon Elastic Transcoder](https://aws.amazon.com/jp/elastictranscoder/)を使えば、動画のフォーマットやサイズ変更が簡単にできますが、費用が高いのでマシンパワーを必要とするような変換でなければLambdaで処理したほうが手軽で良いと思います。
以前書いた記事 ([スマートホームカメラ ATOM Cam をセットアップ](https://qiita.com/revsystem/items/d0821a437d4538d039bf))で使用してるATOM Camの映像をNAS経由でS3に転送しAmazon Elastic Transcoderで動画フォーマットを変換する処理をつくったところ、あっというまに数千円消費してしまいました。こういう用途にはffmpegを使ったほうが良いようです。

## このサンプルでできること
[ffmpeg-aws-lambda-layer](https://github.com/serverlesspub/ffmpeg-aws-lambda-layer) に用意されているサンプルプロジェクトをデプロイすると、S3に ffmpeg-layer-example-uploadbucket-xxxx と  ffmpeg-layer-example-resultbucket-xxxx (xxxxはランダムな文字列、以下同様)というバケットが生成され、Lambda関数との接続やIAM roleの設定が自動的に行われます。
ffmpeg-layer-example-uploadbucket-xxxx に動画ファイルがアップロードされると、自動的にffmpeg-layer-example-resultbucket-xxxx にサムネイル画像が生成さます。

図にするとこのようになります。
![ffmpeg-aws-lambda-layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/a58c1610-0fcd-2c2f-f8d6-62e0c61df272.png)


## 準備するもの
 - [ffmpeg-aws-lambda-layer](https://github.com/serverlesspub/ffmpeg-aws-lambda-layer) に書かれているもの

```text
- Unix Make environment
- AWS command line utilities (just for deployment)
```

今回は以下の環境で動作確認しました。

```shell
$ sw_vers 
ProductName:	Mac OS X
ProductVersion:	10.15.7
BuildVersion:	19H2
```

```shell
$ aws --version
aws-cli/2.0.57 Python/3.7.4 Darwin/19.6.0 exe/x86_64
```

- AWSアカウント
    - Lambda, S3, CloudFormationを操作できるroleが必要です。Administrator権限があれば問題無し。

## 参考情報
- [AWS LambdaでFFmpegを使って動画からサムネイルを作成する](https://qiita.com/yu-croco/items/49f8600a1f7acbdac96f)
- [ffprobeで動画を解析する](https://qiita.com/makotok7/items/0e7d2f941e5da8cec8d1)
- [AWS Lambda(Node.js) + ffmpeg でエンコード](https://xp-cloud.jp/blog/2018/05/18/3345/)
- [カスタムAWS Lambdaランタイムで音声ファイルを生成・変換させてみる](http://blog.kumoto.jp/2019/01/08/custom-aws-lambda-runtimes/)
- [Lambda Layerの基本的な仕組みを確認する #reinvent](https://dev.classmethod.jp/articles/lambda-layer-basics-how-it-works/)


## 準備
### ffmpeg-aws-lambda-layer を git clone

```shell
git clone https://github.com/serverlesspub/ffmpeg-aws-lambda-layer.git
```

### テンポラリ S3 バケットの準備
~/.aws/credentials の default profile に指定した region と同じリージョンにバケットを作成します。


## サンプルプロジェクトをdeploy
DEPLOYMENT_BUCKET オプションの値に、テンポラリ S3 バケットのバケット名を指定します。

```shell
cd ffmpeg-aws-lambda-layer
make deploy-example DEPLOYMENT_BUCKET=<BUCKET NAME>
```

make コマンドを実行すると以下のようなログが流れます。

```
cd example && \
		make deploy DEPLOYMENT_BUCKET=<BUCKET NAME> LAYER_STACK_NAME=ffmpeg-lambda-layer
mkdir -p build
aws cloudformation package --template-file template.yaml --output-template-file output.yaml --s3-bucket net.rev-system.nas
Uploading to d5886731828e201e755325e950d28eab  1742 / 1742.0  (100.00%)
Successfully packaged artifacts and wrote output template to file output.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /tmp/ffmpeg-aws-lambda-layer/example/output.yaml --stack-name <YOUR STACK NAME>
aws cloudformation deploy --template-file output.yaml --stack-name ffmpeg-layer-example --capabilities CAPABILITY_IAM --parameter-overrides LambdaLayer=arn:aws:lambda:ap-northeast-1:************:layer:ffmpeg:1

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - ffmpeg-layer-example
aws cloudformation describe-stacks --stack-name ffmpeg-layer-example --query Stacks[].Outputs --output table
--------------------------------------------------------------------------------------------
|                                      DescribeStacks                                      |
+-------------------+----------------+-----------------------------------------------------+
|    Description    |   OutputKey    |                     OutputValue                     |
+-------------------+----------------+-----------------------------------------------------+
|  Upload S3 bucket |  UploadBucket  |  ffmpeg-layer-example-uploadbucket-xxxxxxxxxxx      |
|  Results S3 bucket|  ResultsBucket |  ffmpeg-layer-example-resultsbucket-yyyyyyyyyyyy    |
+-------------------+----------------+-----------------------------------------------------+
````

ここで、以下のリソースが作成されます。

| リソース種類 | 名称例 |
|:--|:--|
| Lambda関数 | ffmpeg-layer-example-ConvertFileFunction-nnnnnnnnnnnn |
| Lambdaレイヤー | ffmpeg |
| S3 バケット(アップロード用) | ffmpeg-layer-example-uploadbucket-xxxxxxxxxxx |
| S3 バケット(変換済み) | ffmpeg-layer-example-resultsbucket-yyyyyyyyyyyy |
| CloudFrontスタック | ffmpeg-layer-example |
| IAMロール | ffmpeg-layer-example-ConvertFileFunctionRole-zzzzzzzzzzzz| 


アップロード用バケットには、オブジェクトが作成された際にLambdaに通知されるイベント通知が設定されます。
![lambda005-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/a1d71e61-0dfb-322f-871e-a260d87d1dc7.png)

Lambdaファンクションにレイヤーが追加されます。
![lambda006-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/27bc7915-482e-af36-99e2-0c941d1ac706.png)




## サムネイルの生成
S3バケット ffmpeg-layer-example-uploadbucket-xxxxxxxxxxx に適当な動画ファイルをアップロードします。
しばらくすると ffmpeg-layer-example-resultsbucket-yyyyyyyyyyyy にサムネイル画像が出力されます。


アップロードバケットに動画ファイルをアップロードしてみます。
![lambda003-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/16667ba5-631d-2b71-b1a1-8e0321c9203f.png)

すると10秒ほどでサムネイルが生成されました。
![lambda004-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/75408cac-b6cb-ab3f-03fc-f5a107d797ea.png)

アップロードした動画は、[ビデオ撮影・空撮・編集 / HYBRID CREATIVE MOVIE サクラ](https://www.home-movie.biz/free_movie.html)のフリー素材 [風景・木・風にそよぐ（1）](https://www.home-movie.biz/mov/hts-samp003.mp4)を使用しました。

生成されたサムネイルはこちらです。20KBの小さな画像生成されました。
![mov_hts-samp003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/bb58f2e1-d66c-313c-26c0-32733ccb0844.jpeg)



### サムネイルのサイズやその他の出力

動画を別フォーマットにしたり、ffprobeで動画ファイルのメタ情報を出力する場合は、`ffmpeg-aws-lambda-layer/example/src/index.js` を変更して再度デプロイすることで、メタ情報の出力などいろいろ試せます。


## Lambdaレイヤーのみ作成する

アップロード/変換済み用S3バケットやLambda関数はデプロイせず、Lambdaレイヤーのみデプロイする場合は以下のコマンドを実行します。
DEPLOYMENT_BUCKET オプションの値に、テンポラリ S3 バケットのバケット名を指定します。

```shell
cd ffmpeg-aws-lambda-layer
make deploy DEPLOYMENT_BUCKET=<BUCKET NAME>
```

Lambdaレイヤーのみデプロイされるので、任意のLambda関数にこのレイヤーを追加することで使用できるようになります。


## 作成されたリソースの削除

CloudFormationにあるスタックを削除することで、作成されたリソースが削除されます。S3バケットにオブジェクトが残っていると削除時にエラーとなるので事前に削除が必要です。
![lambda007-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61809/cefd5718-94b5-986c-59c3-ac6b87e1b23a.png)