Skip to content

mokashiswapnil/lambda-ffmpeg-transcoder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lambda-ffmpeg-transcoder

AWS Lambda + FFmpeg layer for serverless video transcoding. Reduces transcoding time from hours to minutes through parallel Lambda invocations — one Lambda per output resolution, all running concurrently.

Real benchmark: a 30-second source video transcoded to 4 resolutions (1080p, 720p, 480p, 360p) completes in ~2 minutes total using parallel Lambdas. The same job on a traditional server takes 10–30 minutes due to sequential processing and CPU saturation.


Architecture

Client / Your Server
        │
        │  POST { inputUrl, resolutions: ["1080p","720p","480p","360p"] }
        ▼
┌─────────────────────┐
│  Orchestrator Lambda │  ← single entry point
│  (orchestrator.js)  │
└──────────┬──────────┘
           │  Invokes all 4 in parallel (Promise.all)
   ┌───────┼───────┬──────────┐
   ▼       ▼       ▼          ▼
[1080p] [720p]  [480p]     [360p]   ← each is a separate Lambda execution
   │       │       │          │       each has its own CPU + memory
   └───────┴───────┴──────────┘
                   │
                   ▼
             Amazon S3
         (outputs uploaded)

Each transcoder Lambda (transcoder.js):

  1. Downloads the source video (HTTPS or s3://)
  2. Runs the FFmpeg command you supply (with {input} / {output} placeholders)
  3. Uploads the result to S3
  4. Returns the S3 key and URL

The orchestrator Lambda (orchestrator.js) fans out all resolutions in parallel using Promise.all, waits for all to finish, and returns a summary with total wall-clock time.


Why Traditional Server Transcoding Is Slow

The CPU Bottleneck Problem

On a traditional server (EC2, VPS, bare metal), video transcoding is one of the most CPU-intensive operations possible. libx264 encoding pegs CPU cores at 100% utilization for the entire duration.

Traditional Server — Sequential Processing
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

CPU: [████████████ 1080p ████████████][████ 720p ████][██ 480p ██][█ 360p █]
      ←─── 8 min ──────────────────→←── 5 min ──→←─3 min─→←2 min→
                                                         Total: ~18 minutes

Problem 1: Sequential jobs. While transcoding 1080p, all other resolutions wait.
Problem 2: CPU saturated at 100% during each job — server can't serve other requests.
Problem 3: If 3 videos arrive simultaneously, wait time TRIPLES (54 min for 3rd video).
Problem 4: You pay for the server 24/7 even when it's idle.

The Cascading Degradation Effect

The real damage happens under load:

Concurrent upload requests Wait time for last video (4 resolutions)
1 video ~18 min
2 videos ~36 min
3 videos ~54 min
5 videos ~90 min

Each additional concurrent transcode doubles or triples the wait for everyone else. You either:

  • Under-provision → users wait hours
  • Over-provision → you pay for idle CPU 95% of the time

Lambda Parallel Architecture — Wall Clock vs CPU Time

Lambda — Parallel Processing
━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Lambda 1 (1080p): [████████████ 8 min ████████████]
Lambda 2 (720p):  [████ 5 min ████]
Lambda 3 (480p):  [██ 3 min ██]
Lambda 4 (360p):  [█ 2 min █]
                  ←──── all running simultaneously ────→
                  Total wall-clock time: ~8 min (longest job)

With 3x concurrent videos: still ~8 min (12 independent Lambdas run in parallel)

Key insight: Lambda gives each invocation its own isolated CPU. 4 resolutions = 4 separate CPUs running simultaneously. Cost scales with actual compute used, not provisioned capacity.


Setup

1. Add the FFmpeg Lambda Layer

Use the public FFmpeg layer for your region (no build required):

Region Layer ARN
us-east-1 arn:aws:lambda:us-east-1:145553500800:layer:ffmpeg:1
eu-west-1 arn:aws:lambda:eu-west-1:145553500800:layer:ffmpeg:1

Or build your own: https://github.com/nicholasess/aws-lambda-ffmpeg-layer

The layer places the ffmpeg binary at /opt/bin/ffmpeg.

2. Deploy Transcoder Lambda

npm install
zip -r function.zip src/ package.json node_modules/

aws lambda create-function \
  --function-name ffmpeg-transcoder \
  --runtime nodejs20.x \
  --role arn:aws:iam::YOUR_ACCOUNT:role/lambda-transcoder-role \
  --handler src/transcoder.handler \
  --zip-file fileb://function.zip \
  --timeout 900 \
  --memory-size 3008 \
  --layers arn:aws:lambda:us-east-1:145553500800:layer:ffmpeg:1

3. Deploy Orchestrator Lambda

aws lambda create-function \
  --function-name ffmpeg-orchestrator \
  --runtime nodejs20.x \
  --role arn:aws:iam::YOUR_ACCOUNT:role/lambda-transcoder-role \
  --handler src/orchestrator.handler \
  --zip-file fileb://function.zip \
  --timeout 900 \
  --memory-size 512 \
  --environment Variables="{TRANSCODER_FUNCTION_NAME=ffmpeg-transcoder}"

4. Enable Lambda Function URL

aws lambda create-function-url-config \
  --function-name ffmpeg-orchestrator \
  --auth-type NONE

# Response includes:
# "FunctionUrl": "https://abcdefg.lambda-url.us-east-1.on.aws/"

IAM Role — Required Permissions

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject"],
      "Resource": "arn:aws:s3:::YOUR-BUCKET/*"
    },
    {
      "Effect": "Allow",
      "Action": ["lambda:InvokeFunction"],
      "Resource": "arn:aws:lambda:*:*:function:ffmpeg-transcoder"
    }
  ]
}

Usage — Lambda Function URL

Single Resolution (Transcoder directly)

curl -X POST https://YOUR-TRANSCODER-URL.lambda-url.us-east-1.on.aws/ \
  -H "Content-Type: application/json" \
  -d '{
    "command": "-i {input} -vf scale=1280:720 -b:v 2500k -b:a 128k -c:v libx264 -c:a aac -movflags faststart -y {output}",
    "inputUrl": "https://example.com/raw-video.mp4",
    "outputBucket": "my-transcoded-videos",
    "outputKey": "videos/abc123/720p.mp4",
    "outputFormat": "mp4"
  }'

Response:

{
  "success": true,
  "outputBucket": "my-transcoded-videos",
  "outputKey": "videos/abc123/720p.mp4",
  "url": "https://my-transcoded-videos.s3.amazonaws.com/videos/abc123/720p.mp4",
  "transcodingTime": "118.4s"
}

Parallel Multi-Resolution (Orchestrator)

curl -X POST https://YOUR-ORCHESTRATOR-URL.lambda-url.us-east-1.on.aws/ \
  -H "Content-Type: application/json" \
  -d '{
    "inputUrl": "https://example.com/raw-video.mp4",
    "outputBucket": "my-transcoded-videos",
    "outputPrefix": "videos/abc123",
    "resolutions": ["1080p", "720p", "480p", "360p"],
    "outputFormat": "mp4"
  }'

Response:

{
  "success": true,
  "totalTime": "127.3s",
  "succeeded": 4,
  "failed": 0,
  "outputs": [
    { "resolution": "1080p", "outputKey": "videos/abc123/1080p.mp4", "url": "https://..." },
    { "resolution": "720p",  "outputKey": "videos/abc123/720p.mp4",  "url": "https://..." },
    { "resolution": "480p",  "outputKey": "videos/abc123/480p.mp4",  "url": "https://..." },
    { "resolution": "360p",  "outputKey": "videos/abc123/360p.mp4",  "url": "https://..." }
  ]
}

Custom FFmpeg Command

Any valid FFmpeg argument string works — use {input} and {output} as file placeholders:

{
  "command": "-i {input} -vf \"scale=1920:1080,fps=30\" -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 192k -movflags faststart -y {output}",
  "inputUrl": "s3://my-raw-bucket/uploads/video.mov",
  "outputBucket": "my-transcoded-videos",
  "outputKey": "videos/abc123/1080p.mp4"
}

Extract audio only:

{
  "command": "-i {input} -vn -c:a libmp3lame -b:a 192k -y {output}",
  "inputUrl": "https://example.com/video.mp4",
  "outputBucket": "my-audio-bucket",
  "outputKey": "audio/abc123/audio.mp3",
  "outputFormat": "mp3"
}

Generate animated GIF:

{
  "command": "-i {input} -vf \"fps=10,scale=480:-1:flags=lanczos\" -loop 0 -y {output}",
  "inputUrl": "https://example.com/clip.mp4",
  "outputBucket": "my-gifs-bucket",
  "outputKey": "gifs/abc123/preview.gif",
  "outputFormat": "gif"
}

Configuration Reference

Lambda Handler Memory Timeout Purpose
ffmpeg-transcoder src/transcoder.handler 3008 MB 900s Runs FFmpeg, uploads to S3
ffmpeg-orchestrator src/orchestrator.handler 512 MB 900s Fans out parallel jobs

Built-in Resolution Presets

Preset Scale Video Bitrate Audio Bitrate
1080p 1920×1080 4000k 192k
720p 1280×720 2500k 128k
480p 854×480 1000k 128k
360p 640×360 600k 96k
240p 426×240 300k 64k

License

MIT

About

AWS Lambda + FFmpeg layer for serverless parallel video transcoding - reduces hours to minutes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors