Introduction
In the world of video streaming and content delivery, video codec compatibility remains a significant challenge. While H.265 (HEVC) offers superior compression efficiency, H.264 (AVC) still dominates in terms of device compatibility and browser support. This blog post details the journey of building a serverless video format converter using AWS Lambda that automatically processes uploaded videos, detects H.265 encoded content, and converts it to the more widely compatible H.264 format.
The Problem
Modern devices and cameras increasingly default to H.265 encoding for its efficiency benefits. However, many web browsers, older devices, and streaming platforms still require H.264 for playback. This creates a need for automatic video transcoding in content pipelines. Traditional solutions often involve dedicated servers running FFmpeg, which can be costly and require constant maintenance.
The Solution: Serverless Video Processing
We built a Lambda function (ksm-video-formatter-lambda) that:
- Triggers automatically when MP4 files are uploaded to an S3 bucket
- Detects the video codec using FFmpeg's ffprobe
- Converts H.265 videos to H.264 format
- Saves the processed file with an _output suffix
- Handles H.264 videos by simply copying them with the suffix
This serverless approach offers several advantages:
- Cost-effective: Pay only for actual processing time
- Scalable: Handles multiple uploads simultaneously
- Maintenance-free: No servers to manage
- Event-driven: Processes videos immediately upon upload
Technical Architecture
Core Components
- AWS Lambda Function: The heart of the system, written in Python
- S3 Buckets: Source bucket for uploads and destination for processed videos
- Lambda Layers: Contains Python dependencies and FFmpeg binaries
- Terraform: Infrastructure as Code for consistent deployments
- GitHub Actions: CI/CD pipeline for automated deployments
The Lambda Function
# Core logic flow
def lambda_handler(event, context):
# 1. Extract S3 event details
# 2. Download video to /tmp
# 3. Use ffprobe to detect codec
# 4. If H.265: convert to H.264
# 5. If H.264: copy as-is
# 6. Upload result with _output suffix
# 7. Clean up temporary files
Challenges and Solutions
Challenge 1: FFmpeg in Lambda Environment
The biggest hurdle was getting FFmpeg to work within Lambda's constraints. Lambda functions don't include video processing tools by default, and the execution environment has strict size limits.
Solution: We created Lambda layers containing FFmpeg binaries:
- Downloaded statically-compiled FFmpeg binaries
- Created build scripts for reliable layer creation
- Implemented S3-based distribution for team access
Challenge 2: Lambda Layer Size Limits (70MB)
FFmpeg binaries alone can exceed 40MB, and combined with Python dependencies, we hit Lambda's 70MB layer limit.
Solution: We implemented multiple strategies:
- Dual-layer approach: Separate layers for Python dependencies and FFmpeg
- Optimized builds: Compressed binaries and removed unnecessary components
- Minimal dependencies: Carefully selected only essential Python packages
Challenge 3: Reliable FFmpeg Distribution
Direct downloads of FFmpeg during CI/CD builds proved unreliable due to rate limiting and network issues.
Solution: We created an S3-based distribution system:
# build-layer-s3.sh
aws s3 cp s3://${FFMPEG_S3_BUCKET}/${FFMPEG_S3_KEY} ffmpeg.tar.xz
tar -xf ffmpeg.tar.xz -C python/bin/
Challenge 4: Path and Permissions
Lambda's execution environment required specific paths and permissions for FFmpeg execution.
Solution:
- Placed binaries in /opt/bin/ (Lambda layer path)
- Set executable permissions in build scripts
- Configured PATH environment variable in Lambda
Implementation Details
Build Scripts Evolution
We created multiple build scripts to handle different scenarios:
- build-layer-minimal.sh: Python dependencies only
- build-layer-optimized.sh: Combined layer with compression
- build-separate-layers.sh: Dual-layer approach
- build-layer-s3.sh: S3-based FFmpeg download
Terraform Configuration
resource "aws_lambda_function" "video_formatter" {
function_name = "ksm-video-formatter-lambda"
layers = [
aws_lambda_layer_version.python_deps.arn,
aws_lambda_layer_version.ffmpeg.arn
]
environment {
variables = {
PATH = "/opt/bin:/opt/python/bin:${PATH}"
}
}
}
CI/CD Pipeline
Our GitHub Actions workflow handles:
- Dependency installation
- Layer building with fallback strategies
- Terraform deployment
- Environment-specific configurations
Lessons Learned
- Start with Layer Strategy: Plan your Lambda layer approach early. The 70MB limit affects architecture decisions.
- Binary Distribution Matters: Don't rely on external downloads during CI/CD. Host critical binaries in your own S3 bucket.
- Test Locally First: Use Docker with Lambda runtime images to test FFmpeg integration before deployment.
- Monitor Cold Starts: Large layers increase cold start times. Monitor and optimize based on your latency requirements.
- Error Handling is Crucial: Video processing can fail for many reasons. Implement comprehensive error handling and logging.
Performance Considerations
- Cold Start: ~3-5 seconds with FFmpeg layer
- Processing Time: Varies by video size and complexity
- Memory Usage: 3GB recommended for HD video processing
- Timeout: Set to 15 minutes for large files
Cost Analysis
For a typical use case processing 1,000 videos/month:
- Average video: 100MB (5 minutes HD)
- Processing time: 30 seconds per video
- Total compute: 500 minutes
- Estimated cost: ~$10-15/month
Compared to an EC2 instance running 24/7: ~$50-100/month
Future Enhancements
- Parallel Processing: Split large videos for faster processing
- Format Detection: Support more video formats beyond MP4
- Quality Options: Allow customizable output quality settings
- Progress Tracking: Implement progress notifications via SNS
- Batch Processing: Handle multiple videos in a single invocation
Code Repository Structure
ksm-video-formatter-lambda/
├── src/
│ └── lambda_function.py
├── terraform/
│ ├── main.tf
│ ├── lambda.tf
│ └── variables.tf
├── .github/
│ └── workflows/
│ ├── cd.yml
│ └── cd-prod.yml
├── scripts/
│ ├── build-layer-s3.sh
│ ├── build-separate-layers.sh
│ └── build-layer-optimized.sh
└── requirements.txt
Conclusion
Building a serverless video processor with AWS Lambda presents unique challenges, but the benefits of scalability, cost-effectiveness, and maintenance-free operation make it worthwhile. The key to success lies in careful planning of Lambda layers, reliable binary distribution, and comprehensive error handling.
This project demonstrates that complex media processing tasks, traditionally reserved for dedicated servers, can be effectively handled in a serverless architecture. As Lambda limits continue to increase and new features are added, serverless video processing will become even more attractive for production workloads.
Key Takeaways
- Serverless video processing is viable and cost-effective
- Lambda layers are crucial for managing dependencies
- S3-based binary distribution ensures reliable deployments
- Proper error handling and monitoring are essential
- The 70MB layer limit requires creative solutions
Whether you're building a content platform, implementing a media pipeline, or just need automated video conversion, this serverless approach provides a solid foundation that scales with your needs while keeping costs under control.