AWS Lambda(Node.js) + ffmpeg でエンコード

動画のサイズや形式を変更したり、音楽だけ抜き出したりするのに"Amazon Elastic Transcoder"を使用している方は多いと思います。
このサービスは驚異のマシンパワーでガンガンエンコードしてくれるため非常に便利なのですが、利用料金もガンガンかかってきます。
今回は料金を抑えるためにAWS Lambda上でエンコードする方法をお伝えします。

使用するものは次の4つです。
AWS Lambda…今回はNode.jsを使用しますがAWS SDKLinuxコマンドを叩くだけなので何でもOKです。
Amazon S3…今回はエンコード元、先ファイルを置くのに使用します。
ffmpeg
・動画ファイル…今回はMP4を使用しますが、MP3などffmpegが対応していれば何でもOKです。

ffmpegについてはアマゾンも推奨しているツールになってます。
音声合成マークアップ言語(SSML)のリファレンス

 

ffmpegの用意

公式サイトからLinux用のffmpegをダウンロードします
FFmpeg

Linux用のffmpegをダウンロード

 

x86_64buildをダウンロードします。

x86_64buildをダウンロード

 

ひとまず適当なフォルダへ解凍しておきます。

フォルダへ解凍

 

S3の用意

適当な場所へエンコードしたいMP4ファイルをアップロードしてください。

S3の用意

 

Lambdaの用意

Lambda関数をNode.jsで作成してください。
ソースは上書きしてしまうので、空のLambdaでも何かのテンプレートを使用しても、どっちでも大丈夫です。

Lambdaの用意

 

次にソースをLambdaの中身のソースを作ります。下記の構成でファイルを配置してください。
フォルダ
├index.js
└bin
ffmpeg

 

ffmpegはダウンロードして解凍したファイル1つだけを持ってきてください。
他のフォルダやindex.jsは新規作成してください。

ffmpegダウンロード

 

index.jsに次のソースを貼り付けます。

'use strict';

const fs = require('fs');
const aws = require('aws-sdk');
var s3 = new aws.S3();
let execSync = require('child_process').execSync;

const getObject = function(bucket, key, resolve, preserve) {
  let params = {
    Bucket: bucket,
    Key: key
  };
  s3.getObject(params, function(error, data) {
    try {
      if (error) {
        preserve(error);
      } else {
        resolve(data);
      }
    } catch(e) {
      preserve(e);
    }
  });
};

const putObject = function(body, bucket, key, resolve, preserve) {
  let params = {
    Body: body,
    Bucket: bucket,
    Key: key
  };
  s3.putObject(params, function(error, data) {
    try {
      if (error) {
        preserve(error);
      } else {
        resolve(data);
      }
    } catch(e) {
      preserve(e);
    }
  });
};

exports.handler = (event, context, callback) => {
  try {
    getObject('<バケット名>', '<パス名>/src.mp4', function(srcData) {
      fs.writeFileSync('/tmp/srcData.mp4', srcData.Body);

      process.env.PATH += ':/var/task/bin';
      execSync('ffmpeg -i /tmp/srcData.mp4 -vf scale=320:240 /tmp/destData.mp4');

      var fileStream = fs.createReadStream('/tmp/destData.mp4');
      fileStream.on('error', function(error) {
        callback(JSON.stringify(error));
      });

      putObject(fileStream, '<バケット名>', '<パス名>/desc.mp4', function(data) {
        callback(null, 'OK');
      }, function(error) {
        callback(JSON.stringify(error));
      });
    }, function(error) {
      callback(JSON.stringify(error));
    });
  } catch(e) {
    callback(JSON.stringify(e));
  }
};

 

バケット名>、<パス名>の部分は上で用意したS3のファイルの物に置き換えてください。
ブログの関係で”>”も全角になっていますので、半角にしておいてください。

フォルダの中身だけをzipにしてS3へアップロードします(index.js等を入れたフォルダは含めないでください)。
また、パスやファイル名に日本語は含めないでください。

S3のファイルの物に置き換え

 

Lambdaで実行

"コードエントリタイプ"を"Amazon S3からのファイルのアップロード"へ
"S3リンクのURL"へアップロードしたファイルのURLを入力して保存します。

Lambdaで実行

 

テストを押して実行してみましょう。
パラメータを入力する画面が出てきますが、パラメータは使わないので適当で大丈夫です。

これで上手くいけばいいのですが、デフォルトのLambdaは3秒を越えると強制終了してしまいます。
ffmpegの処理は重いので3秒以内というのは難しいところがあり、次のようなエラーになることが多いです。

ffmpegの処理 エラー

 

Lambdaの下の方にタイムアウトを変更する項目があるので、伸ばして再実施してみてください。

Lambda タイムアウトを変更

 

正常に終わればS3に変換後MP4ファイルができています。
ダウンロードして再生してみましょう。

S3に変換後MP4ファイル

 

変換したことにより動画のサイズが縮んでいることが確認できます。

変換後

 

今回は動画のサイズを変えてみましたが、MP3に変換して音声だけにしたり、再生速度を変えたり、とffmpegのオプションを変更してやるだけでできます。
メディアを扱ったサービスを展開している方は是非取り入れてみてください。

 

 

 

AWS相談会