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 の認証情報を外部ファイルにまとめました。
{
"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 にアップロードするサンプルコードです。
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 のサムネイル生成コマンド
- Extract frames from HTTP streamed video without downloading entire file - SrackExchange
- ffmpeg Documentation