Edited at

Lambda(Go)でffmpegの出力をS3にアップロード

AWS Lambda で Golang がサポートされて早1年*

未だ Go で作った Lambda関数 で ffmpeg を動かしてみたという記事が見当たらなかったので書きました。

今回は ffmpeg で生成した動画サムネイルを S3 にアップロードするサンプルコードを紹介します。


サンプルコード


ディレクトリ構成

作業ディレクトリの構成は以下の通りです。


作業ディレクトリのツリーマップ

.

├── ffmpeg
├── credentials.json
└── main.go


ffmpeg

ffmpeg は以下の公式サイトより取得できます。

https://www.ffmpeg.org

Lambda の OS は Amazon Linux なので、DLページでは Linux のパッケージを入手します。

今回は Lambda 内でローカルに呼び出すので、Linux Static Builds から適当なパッケージを選びます。

(DLしたパッケージファイルを展開すると色々ファイルが入っていますが、今回利用するのはffmpegのみです)


credentials.json

今回は Lambda や S3 の認証情報を外部ファイルにまとめました。


credentials.json

{

"accessKeyId": "Your Access Key",
"secretAccessKey": "Your Secret Key",
"region": "Your Region (ap-northeast-1等)",
"bucketName": "S3 Bucket Name",
"keyPath": "S3 Key Path",
}


main.go

ffmpeg で生成した動画サムネイルを S3 にアップロードするサンプルコードです。


main.go

package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"

"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
)

var c = readCredentialsJson()
var client = getS3Client()

var videoURL string = "Target Video URL"
var thumbName string = "thumbName.png"
var frameSecond string = "0"

func main() {
lambda.Start(lambda2s3)
}

func lambda2s3() (string, error) {
f := createThumb()
putFileToS3(f)

return fmt.Sprintf("done!"), nil
}

// ffmpegでサムネイルを生成
func createThumb() *os.File {
// 環境変数を設定
os.Setenv("PATH", os.Getenv("PATH")+os.Getenv("LAMBDA_TASK_ROOT"))

// サムネイルを生成し、/tmpフォルダに一時保存
err := exec.Command("ffmpeg", "-i", videoURL, "-ss", frameSecond, "-frames:v", "1", "/tmp/"+thumbName).Run()
if err != nil {
fmt.Println(err)
}

// 一時保存したサムネイルを読み出す
f, err := os.Open("/tmp/" + thumbName)
if err != nil {
fmt.Println(err)
}
return f
}

// S3にアップロード
func putFileToS3(f *os.File) {
client.PutObject(&s3.PutObjectInput{
ACL: aws.String("public-read"),
Bucket: aws.String(c.BucketName),
Key: aws.String("/" + c.KeyPath + thumbName),
ContentType: aws.String("image/png"),
Body: aws.ReadSeekCloser(f),
})
}

// S3クライアントを作成
func getS3Client() *s3.S3 {
sess := session.Must(session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(c.AccessKeyId, c.SecretAccessKey, ""),
Region: aws.String(c.Region),
}))
client := s3.New(sess)

return client
}

// 認証情報の構造体
type Credentials struct {
AccessKeyId string `json:"accessKeyId"`
SecretAccessKey string `json:"secretAccessKey"`
Region string `json:"region"`
BucketName string `json:"bucketName"`
KeyPath string `json:"keyPath"`
}

// 認証情報JSONを読み込む
func readCredentialsJson() Credentials {
jsonString, err := ioutil.ReadFile("credentials.json")
if err != nil {
fmt.Println(err)
}

c := new(Credentials)
json.Unmarshal(jsonString, c)

return Credentials{
AccessKeyId: c.AccessKeyId,
SecretAccessKey: c.SecretAccessKey,
Region: c.Region,
BucketName: c.BucketName,
KeyPath: c.KeyPath,
}
}



実行コマンド

念のため、サンプルコードの実行コマンドも掲載しておきます。

$ GOOS=linux go build -o main

でコンパイルして、

$ zip -r myfunc.zip main ffmpeg

で圧縮。出来た圧縮ファイル myfunc.zip を Lambda にアップロードすれば Lambda関数 の完成です。


一部解説


環境変数の設定

// 環境変数を設定

os.Setenv("PATH", os.Getenv("PATH")+os.Getenv("LAMBDA_TASK_ROOT"))

同梱している ffmpeg を呼び出すために、作業ディレクトリにPATHを通す必要があります。

今回のサンプルでは os.Setenv() と os.Getenv() で設定しました。


/tmp ディレクトリの利用

// サムネイルを生成し、/tmpフォルダに一時保存

err := exec.Command("ffmpeg", "-i", videoURL, "-ss", frameSecond, "-frames:v", "1", "/tmp/" + thumbName).Run()
if err != nil {
fmt.Println(err)
}

Lambda では、/tmp という一時作業ディレクトリが提供されています。

一般に、Lambda を S3 と連携させる際によく利用されます。

今回は、ffmpeg で出力したサムネイルの一時保存先として利用しました。


参考記事


ffmpeg のサムネイル生成コマンド


Golang での外部コマンドの実行


/tmp ディレクトリについて


Node.js で ffmpeg を利用した例