├── .gitignore ├── LICENSE.txt ├── Makefile ├── README-SAR.md ├── README.md ├── example ├── .gitignore ├── Makefile ├── src │ ├── child-process-promise.js │ ├── index.js │ └── s3-util.js ├── template-sar.yaml └── template.yaml └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.pdf 3 | *.png 4 | *.svg 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | * These scripts: [MIT](https://opensource.org/licenses/MIT) 4 | * FFmpeg: GPLv2.1 , John Van Sickle's static build GPL v3 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | STACK_NAME ?= ffmpeg-lambda-layer 2 | 3 | clean: 4 | rm -rf build 5 | 6 | build/layer/bin/ffmpeg: 7 | mkdir -p build/layer/bin 8 | rm -rf build/ffmpeg* 9 | cd build && curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | tar x 10 | mv build/ffmpeg*/ffmpeg build/ffmpeg*/ffprobe build/layer/bin 11 | 12 | build/output.yaml: template.yaml build/layer/bin/ffmpeg 13 | aws cloudformation package --template $< --s3-bucket $(DEPLOYMENT_BUCKET) --output-template-file $@ 14 | 15 | deploy: build/output.yaml 16 | aws cloudformation deploy --template $< --stack-name $(STACK_NAME) 17 | aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query Stacks[].Outputs --output table 18 | 19 | deploy-example: 20 | cd example && \ 21 | make deploy DEPLOYMENT_BUCKET=$(DEPLOYMENT_BUCKET) LAYER_STACK_NAME=$(STACK_NAME) 22 | -------------------------------------------------------------------------------- /README-SAR.md: -------------------------------------------------------------------------------- 1 | # FFmpeg/FFprobe Lambda Layer for Amazon Linux 2 AMIs 2 | 3 | Static build of FFmpeg/FFprobe for Amazon Linux 2, packaged as a Lambda layer. Bundles FFmpeg 4.1.3. 4 | 5 | This application provides a single output, `LayerVersion`, which points to a 6 | Lambda Layer ARN you can use with Lambda runtimes based on Amazon Linux 2 (such 7 | as the `nodejs10.x` runtime). 8 | 9 | For an example of how to use the layer, check out 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFmpeg/FFprobe for AWS Lambda 2 | 3 | A Lambda layer containing a static version of FFmpeg/FFprobe utilities from the [`FFmpeg`](https://www.ffmpeg.org/) Linux package, compatible with Amazon Linux 2.x and Amazon Linux 1.x instances (including the `nodejs10.x` runtime, and the updated 2018.03 Amazon Linux 1 runtimes). 4 | 5 | ## Usage 6 | 7 | Absolutely the easiest way of using this is to pull it directly from the AWS Serverless Application repository into a CloudFormation/SAM application, or deploy directly from the Serverless Application Repository into your account, and then link as a layer. 8 | 9 | The `ffmpeg` and `ffprobe` binaries will be in `/opt/bin/` after linking the layer to a Lambda function. 10 | 11 | For more information, check out the [ffmpeg-lambda-layer](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:145266761615:applications~ffmpeg-lambda-layer) application in the Serverless App Repository. 12 | 13 | For manual deployments and custom builds, read below... 14 | 15 | ## Prerequisites 16 | 17 | * Unix Make environment 18 | * AWS command line utilities (just for deployment) 19 | 20 | ## Deploying to AWS as a layer 21 | 22 | This package includes FFmpeg 4.1.3, packaged by John Van Sickle. Please consider supporting him for maintaining statically built FFmpeg packages. For more information, check out 23 | 24 | The output will be in the `result` dir. 25 | 26 | Run the following command to deploy the compiled result as a layer in your AWS account. 27 | 28 | ``` 29 | make deploy DEPLOYMENT_BUCKET= 30 | ``` 31 | 32 | ### configuring the deployment 33 | 34 | By default, this uses `ffmpeg-lambda-layer` as the stack name. Provide a `STACK_NAME` variable when calling `make deploy` to use an alternative name. 35 | 36 | ### example usage 37 | 38 | An example project is in the [example](example) directory. It sets up two buckets, and listens to file uploads on the first bucket to convert and generate thumbnails from uploaded video files. You can deploy it from the root Makefile using: 39 | 40 | ``` 41 | make deploy-example DEPLOYMENT_BUCKET= 42 | ``` 43 | 44 | For more information on using FFmpeg and FFprobe, check out 45 | 46 | ## Author 47 | 48 | Gojko Adzic 49 | 50 | ## License 51 | 52 | * These scripts: [MIT](https://opensource.org/licenses/MIT) 53 | * FFmpeg: GPLv2.1 , John Van Sickle's static build GPL v3 54 | 55 | ## LGPL version 56 | 57 | * [Giuseppe Battista](http://github.com/giusedroid) created a build that contains only LGPL components, for organisations that are concerned about GPL licensing. See it at 58 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | output.yaml 2 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | STACK_NAME ?= ffmpeg-layer-example 2 | LAYER_STACK_NAME ?= ffmpeg-lambda-layer 3 | 4 | LAMBDA_LAYER ?=$(shell aws cloudformation describe-stacks --stack-name $(LAYER_STACK_NAME) --query Stacks[].Outputs[].OutputValue --output text) 5 | SOURCES=$(shell find src/) 6 | 7 | clean: 8 | rm -rf build 9 | 10 | output.yaml: template.yaml $(SOURCES) 11 | mkdir -p build 12 | aws cloudformation package --template-file $< --output-template-file $@ --s3-bucket $(DEPLOYMENT_BUCKET) 13 | 14 | deploy: output.yaml 15 | aws cloudformation deploy --template-file $< --stack-name $(STACK_NAME) --capabilities CAPABILITY_IAM --parameter-overrides LambdaLayer=$(LAMBDA_LAYER) 16 | aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query Stacks[].Outputs --output table 17 | 18 | -------------------------------------------------------------------------------- /example/src/child-process-promise.js: -------------------------------------------------------------------------------- 1 | /*global module, require, console, Promise */ 2 | 'use strict'; 3 | const childProcess = require('child_process'), 4 | spawnPromise = function (command, argsarray, envOptions) { 5 | return new Promise((resolve, reject) => { 6 | console.log('executing', command, argsarray.join(' ')); 7 | const childProc = childProcess.spawn(command, argsarray, envOptions || {env: process.env, cwd: process.cwd()}), 8 | resultBuffers = []; 9 | childProc.stdout.on('data', buffer => { 10 | console.log(buffer.toString()); 11 | resultBuffers.push(buffer); 12 | }); 13 | childProc.stderr.on('data', buffer => console.error(buffer.toString())); 14 | childProc.on('exit', (code, signal) => { 15 | console.log(`${command} completed with ${code}:${signal}`); 16 | if (code || signal) { 17 | reject(`${command} failed with ${code || signal}`); 18 | } else { 19 | resolve(Buffer.concat(resultBuffers).toString().trim()); 20 | } 21 | }); 22 | }); 23 | }; 24 | module.exports = { 25 | spawn: spawnPromise 26 | }; 27 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | 2 | const s3Util = require('./s3-util'), 3 | childProcessPromise = require('./child-process-promise'), 4 | path = require('path'), 5 | os = require('os'), 6 | EXTENSION = process.env.EXTENSION, 7 | THUMB_WIDTH = process.env.THUMB_WIDTH, 8 | OUTPUT_BUCKET = process.env.OUTPUT_BUCKET, 9 | MIME_TYPE = process.env.MIME_TYPE; 10 | 11 | exports.handler = function (eventObject, context) { 12 | const eventRecord = eventObject.Records && eventObject.Records[0], 13 | inputBucket = eventRecord.s3.bucket.name, 14 | key = eventRecord.s3.object.key, 15 | id = context.awsRequestId, 16 | resultKey = key.replace(/\.[^.]+$/, EXTENSION), 17 | workdir = os.tmpdir(), 18 | inputFile = path.join(workdir, id + path.extname(key)), 19 | outputFile = path.join(workdir, id + EXTENSION); 20 | 21 | 22 | console.log('converting', inputBucket, key, 'using', inputFile); 23 | return s3Util.downloadFileFromS3(inputBucket, key, inputFile) 24 | .then(() => childProcessPromise.spawn( 25 | '/opt/bin/ffmpeg', 26 | ['-loglevel', 'error', '-y', '-i', inputFile, '-vf', `thumbnail,scale=${THUMB_WIDTH}:-1`, '-frames:v', '1', outputFile], 27 | {env: process.env, cwd: workdir} 28 | )) 29 | .then(() => s3Util.uploadFileToS3(OUTPUT_BUCKET, resultKey, outputFile, MIME_TYPE)); 30 | }; 31 | -------------------------------------------------------------------------------- /example/src/s3-util.js: -------------------------------------------------------------------------------- 1 | /*global module, require, Promise, console */ 2 | 3 | const aws = require('aws-sdk'), 4 | fs = require('fs'), 5 | s3 = new aws.S3(), 6 | downloadFileFromS3 = function (bucket, fileKey, filePath) { 7 | 'use strict'; 8 | console.log('downloading', bucket, fileKey, filePath); 9 | return new Promise(function (resolve, reject) { 10 | const file = fs.createWriteStream(filePath), 11 | stream = s3.getObject({ 12 | Bucket: bucket, 13 | Key: fileKey 14 | }).createReadStream(); 15 | stream.on('error', reject); 16 | file.on('error', reject); 17 | file.on('finish', function () { 18 | console.log('downloaded', bucket, fileKey); 19 | resolve(filePath); 20 | }); 21 | stream.pipe(file); 22 | }); 23 | }, uploadFileToS3 = function (bucket, fileKey, filePath, contentType) { 24 | 'use strict'; 25 | console.log('uploading', bucket, fileKey, filePath); 26 | return s3.upload({ 27 | Bucket: bucket, 28 | Key: fileKey, 29 | Body: fs.createReadStream(filePath), 30 | ACL: 'private', 31 | ContentType: contentType 32 | }).promise(); 33 | }; 34 | 35 | module.exports = { 36 | downloadFileFromS3: downloadFileFromS3, 37 | uploadFileToS3: uploadFileToS3 38 | }; -------------------------------------------------------------------------------- /example/template-sar.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | Example project demonstrating the deployment of the FFmpeg Layer for AWS Linux 2 runtimes via the AWS Serverless Application Repository, and its reference from within a function. 5 | 6 | Parameters: 7 | ffmpegLambdaVersion: 8 | Type: String 9 | Default: 1.0.0 10 | Description: The semantic version of the ffmpeg layer you wish to deploy. 11 | ConversionFileType: 12 | Type: String 13 | Default: jpg 14 | ConversionMimeType: 15 | Type: String 16 | Default: image/jpeg 17 | ThumbWidth: 18 | Type: Number 19 | Default: 300 20 | Description: Thumbnail width in pixels 21 | Resources: 22 | UploadBucket: 23 | Type: AWS::S3::Bucket 24 | 25 | ResultsBucket: 26 | Type: AWS::S3::Bucket 27 | 28 | ffmpeglambdalayer: 29 | Type: AWS::Serverless::Application 30 | Properties: 31 | Location: 32 | ApplicationId: arn:aws:serverlessrepo:us-east-1:145266761615:applications/ffmpeg-lambda-layer 33 | SemanticVersion: !Ref ffmpegLambdaVersion 34 | 35 | ConvertFileFunction: 36 | Type: AWS::Serverless::Function 37 | Properties: 38 | Handler: index.handler 39 | Timeout: 180 40 | MemorySize: 1024 41 | Runtime: nodejs10.x 42 | CodeUri: src 43 | Layers: 44 | - !GetAtt ffmpeglambdalayer.Outputs.LayerVersion 45 | Policies: 46 | - S3CrudPolicy: 47 | BucketName: !Sub "${AWS::StackName}-*" 48 | Environment: 49 | Variables: 50 | OUTPUT_BUCKET: !Ref ResultsBucket 51 | EXTENSION: !Sub '.${ConversionFileType}' 52 | MIME_TYPE: !Ref ConversionMimeType 53 | THUMB_WIDTH: !Ref ThumbWidth 54 | Events: 55 | FileUpload: 56 | Type: S3 57 | Properties: 58 | Bucket: !Ref UploadBucket 59 | Events: s3:ObjectCreated:* 60 | 61 | Outputs: 62 | UploadBucket: 63 | Description: "Upload S3 bucket" 64 | Value: !Ref UploadBucket 65 | ResultsBucket: 66 | Description: "Results S3 bucket" 67 | Value: !Ref ResultsBucket 68 | -------------------------------------------------------------------------------- /example/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | Example project demonstrating the usage of the FFmpeg Layer for AWS Linux 2 runtimes. 5 | 6 | Parameters: 7 | LambdaLayer: 8 | Type: String 9 | ConversionFileType: 10 | Type: String 11 | Default: jpg 12 | ConversionMimeType: 13 | Type: String 14 | Default: image/jpeg 15 | ThumbWidth: 16 | Type: Number 17 | Default: 300 18 | Description: Thumbnail width in pixels 19 | Resources: 20 | UploadBucket: 21 | Type: AWS::S3::Bucket 22 | 23 | ResultsBucket: 24 | Type: AWS::S3::Bucket 25 | 26 | ConvertFileFunction: 27 | Type: AWS::Serverless::Function 28 | Properties: 29 | Handler: index.handler 30 | Timeout: 180 31 | MemorySize: 1024 32 | Runtime: nodejs10.x 33 | CodeUri: src 34 | Layers: 35 | - !Ref LambdaLayer 36 | Policies: 37 | - S3CrudPolicy: 38 | BucketName: !Sub "${AWS::StackName}-*" 39 | Environment: 40 | Variables: 41 | OUTPUT_BUCKET: !Ref ResultsBucket 42 | EXTENSION: !Sub '.${ConversionFileType}' 43 | MIME_TYPE: !Ref ConversionMimeType 44 | THUMB_WIDTH: !Ref ThumbWidth 45 | Events: 46 | FileUpload: 47 | Type: S3 48 | Properties: 49 | Bucket: !Ref UploadBucket 50 | Events: s3:ObjectCreated:* 51 | 52 | Outputs: 53 | UploadBucket: 54 | Description: "Upload S3 bucket" 55 | Value: !Ref UploadBucket 56 | ResultsBucket: 57 | Description: "Results S3 bucket" 58 | Value: !Ref ResultsBucket 59 | 60 | -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | Static build of FFmpeg/FFprobe for Amazon Linux 2 5 | 6 | Check out https://github.com/serverlesspub/ffmpeg-aws-lambda-layer 7 | for more information. 8 | Resources: 9 | LambdaLayer: 10 | Type: AWS::Serverless::LayerVersion 11 | Properties: 12 | LayerName: ffmpeg 13 | Description: FFMPEG for AWS Lambda 14 | ContentUri: build/layer 15 | CompatibleRuntimes: 16 | - nodejs10.x 17 | - python3.6 18 | - ruby2.5 19 | - java8 20 | - go1.x 21 | LicenseInfo: GPL-2.0-or-later 22 | RetentionPolicy: Retain 23 | 24 | Outputs: 25 | LayerVersion: 26 | Description: Layer ARN Reference 27 | Value: !Ref LambdaLayer 28 | 29 | Metadata: 30 | AWS::ServerlessRepo::Application: 31 | Name: ffmpeg-lambda-layer 32 | Description: > 33 | Static build of FFmpeg/FFprobe for Amazon Linux 2, 34 | packaged as a Lambda layer. Bundles FFmpeg 4.1.3 35 | Author: Gojko Adzic 36 | SpdxLicenseId: GPL-2.0-or-later 37 | LicenseUrl: LICENSE.txt 38 | ReadmeUrl: README-SAR.md 39 | Labels: ['layer', 'lambda', 'ffmpeg', 'ffprobe'] 40 | HomePageUrl: https://github.com/serverlesspub/ffmpeg-aws-lambda-layer 41 | SemanticVersion: 1.0.0 42 | SourceCodeUrl: https://github.com/serverlesspub/ffmpeg-aws-lambda-layer 43 | --------------------------------------------------------------------------------