Networking & Content Delivery
On-the-fly video conversion with Amazon CloudFront, Lambda@Edge, and AWS Elemental MediaConvert
Introduction:
Whether your media library includes long form featured movies or short form “how-to” clips, the popularity of each video asset is typically set by your viewers preference. In order to deliver your online video content, AWS offers multiple solutions that you can use to automate your media supply chain, and streamline your content distribution.
You can further optimize your overall cost of media delivery by deploying an on-the-fly video conversion workflow. Consider your video assets that are viewed infrequently, at a single quality rendition, or never viewed at all. You can find it relevant in models like ‘free to watch’ with ads, where the return on investment for video processing and delivery ties directly to the video content popularity, or in other use cases, for example:
- Unpopular User Generated Content (UGC)
- Clips related to a featured movie, like commentary, or critique review
- Stock footage preview
- Recorded Video Conference calls for future replay
- Infrequent replay of Online DVR (Digital Video Recorder) programs
This blog post introduces a serverless workflow for on-the-fly video conversion; From MP4 video source files stored in an Amazon S3 bucket to HTTP Live Streaming (HLS) served through Amazon CloudFront. The workflow uses Lambda@Edge function to invoke an AWS Elemental MediaConvert job.
On-The-Fly video conversion workflow overview
- End user client sends a request for an HLS video stream to Amazon CloudFront (The request include a resolution and mp4 video source file name parameters in the query string).
- CloudFront forwards your user request to your origin (assuming cache miss), and triggers a Lambda@Edge function on Origin-Request event.
- Lambda@Edge function parses the query string parameters, and attempts to fetch the requested manifest from an HLS stream Amazon S3 media bucket (the Origin).
- If the manifest is found, Lambda@Edge function returns the manifest. If the manifest is not found, Lambda@Edge invokes a MediaConvert job (providing the resolution and mp4 video source file name parameters in a job setting).
- MediaConvert receives the conversion job request, and attempts to fetch the mp4 video source from the media source S3 bucket.
- MediaConvert begins to generate an HLS manifest and segments. It stores them in the HLS Stream S3 bucket, and continues to update the manifest until the conversion process is complete.
- While MediaConvert initiates the conversion job, Lambda@Edge function generates an HLS manifest pointing to an intro video segment, and return it immediately.
- CloudFront forwards the intro manifest back to the end user for playback.
Assuming the end user’s player sends a refresh manifest request, this process begins all over at step 1. This time, Lambda@Edge function will find the newly generated Manifest in the HLS stream S3 bucket, fetch it, and return to CloudFront for delivery to your end user.
Solution deployment pre-requisite
- Create a new AWS account or use an existing account.
CloudFormation deployment walkthrough
Step 1: Choose the Launch Stack button to open the AWS CloudFormation console pre-loaded with the template:
Your CloudFormation Console will open in us-east-1 Region. Choose Next to configure the stack options.
Step 2: Provide names for your media source S3 Bucket, and for your HLS streams S3 Bucket.
The template appends your AWS Account ID as suffix in this format:
your-hls-stream-bucket-<yourAWSAccountId>
your-media-source-bucket-<yourAWSAccountId>
The “Stack name” populates a default name: “on-the-fly-video-convert”. You can keep the default name, or change it to a name of your choice.
Step 3: Use the default configuration options in this step and choose Next. (Optional – specify tags to resources in the Stack).
Step 4: Review and acknowledge the stack configuration, and choose Create stack.
It can take up to 10 minutes for CloudFormation to complete deployment.
CloudFormation deployed resources review
When your CloudFormation deployment is completed, check the outputs tab values and you should see four resources key values:
- Media source S3 Bucket name
- HLS stream S3 Bucket name
- CloudFront distribution endpoint domain name
- Lambda@Edge function ARN (Amazon Resource Name)
Amazon CloudFront
A CloudFront distribution is deployed with the HLS stream S3 bucket as origin; with two additional Cache Behaviors:
0 – *.m3u8 path pattern for HLS manifest requests.
1 – *.ts path pattern for the HLS video segment requests.
For each Cache Behavior, there is a Cache Policy and Origin Request Policy attached.
Both Cache Behavior policies enable CORS headers. While the *.m3u8 Cache Behavior also forwards Query String parameters: width, height, and mediafilename.
Lambda@Edge function
The deployed Lambda@Edge function is associated to CloudFront *.m3u8 Cache Behavior, and triggered on “Origin Request“ Event.
The Lambda@Edge function uses an IAM Role with two permission policies:
- AWS Managed Policy for Basic Lambda Role
- Inline policy that allows GetObject and ListBucket actions from the deployed HLS Stream S3 bucket, MediaConvert CreateJob, and IAM pass-role to MediaConvert, allowing read/write to and from the deployed S3 buckets.
Helper Lambda function
The ‘Helper’ Lambda function runs only once during the CloudFormation stack deployment. It adds AWS MediaConvert API Endpoint as a custom header in CloudFront Origin configuration. CloudFront adds custom headers when forwarding the user request to the Origin. This allows Lambda@Edge function to use these custom headers as parameters at run time, triggered by CloudFront from any one of its global edge locations, not only in us-east-1 Region (where this CloudFormation Stack is deployed).
There are three more CloudFront Origin Custom Headers added in this deployment, and used by Lambda@Edge function at run time: media source S3 bucket name, HLS stream S3 bucket name, and MediaConvert job role.
AWS Elemental MediaConvert API endpoint
This deployment uses MediaConvert API endpoint in us-east-1. You can find it in your MediaConvert Console under Account.
CloudFormation creates a role for MediaConvert to use when a job is invoked. The Role has an Inline Policy that allows GetObject and PutObject actions, restricted to your media source bucket and HLS bucket only.
{
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::your-source-media-bucket-<yourAccountId>/*",
"arn:aws:s3:::your-hls-media-bucket-<yourAccountId>/*"
],
"Effect": "Allow"
}
]
}
Amazon S3 Buckets
In Step 2, you provided names for two Amazon S3 buckets deployed by CloudFormation.
Your HLS stream S3 bucket is deployed with a permission policy attached, that is restricted by:
- CloudFront Origin Access Identity (OAI) to protect the bucket from direct public access.
- Read/write permissions for Lambda@Edge and MediaConvert used in this deployment only.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E2FXXXXXYYYYYY"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-media-bucket/*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::AWSAccountId:role/LambdaFunctionRoleARN",
"arn:aws:iam::AWSAccountId:role/MediaConvertRoleARN"
]
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3::: your-media-bucket/*"
}
]
}
A second bucket policy attached to your Media Source S3 bucket, restricting read access permissions to resources with the attached role that was created for MediaConvert to invoke a job:
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "PolicyForMediaConvertJobRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::Your-Account-ID:role/otf-video-convert-MediaConvertJobRole-Your-Stack-ID"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::Your-Media-Source-Bucket-Your-Account-ID/*"
}
]
}
Test your deployment
- Upload your MP4 media file to your Media Source S3 bucket (yourMediaSourceBucket-<yourAWSAccountId>).
- Create your 6-seconds intro HLS video segment in the following resolutions: 416×234, 640×360, 768×432, 960×540, 1280×720, 1920×1080
- Use this naming format for your intro segment files: ‘intro640x360.ts’ for 640×360 resolution.
- Upload the intro segments you created to your HLS stream bucket.
Your buckets should look like this after uploading your media file and intro segments:
Media Source Bucket:
HLS Stream Bucket:
5. Make an HTTPS request from your HLS player in this format:
https://d1abcd.cloudfront.net/your-media-file768x432.m3u8?width=768&height=432&mediafilename=your-media-file.mp4
- The HTTPS request format require the following parameters in this deployment:
- The manifest file name should be a combination of the source file name (without the mp4 extension), and the stream resolution:
your-media-file768x432.m3u8
. - The resolution parameters in the query string should match the resolution in the manifest file name:
width=768&height=432,
- The source video file name parameter in the query string should point to the file name you want to convert on-the-fly:
your-media-file.mp4
- The manifest file name should be a combination of the source file name (without the mp4 extension), and the stream resolution:
- Change ‘your-media-file’ to the mp4 filename in your media source bucket.
- Change the CloudFront domain to your CloudFront distribution domain name (provided in the CloudFormation Stack Outputs).
Now you can make more requests using the same HTTPS request format, but use different resolutions to retrieve the stream in different renditions.
Cost consideration
Overall, a cost model for on-the-fly video conversion workflow assumes that video assets can be converted only on request, and at a specific resolution.
For example, converting a video library on-the-fly based on this request rate:
- 30% of assets are requested in 6x renditions (3xHD, 3xSD)
- 30% of assets are requested in 4x renditions (2xHD, 2xSD)
- 40% of assets are requested in 2x renditions (1xHD, 1xSD)
Can reduce 36.6% of transcoding time, compared to 100% conversion of the same library:
- 100% of assets converted in 6x renditions (3xHD, 3xSD)
To calculate pricing for services deployed by the CloudFormation in this blog check the following services pricing:
- AWS Elemental Media Convert pricing. This deployment uses MediaConvert accelerated transcoding to reduce the transcoding time required to run a job.
- Amazon CloudFront pricing
- AWS Lambda@Edge pricing
- Amazon S3 pricing
Scale consideration
Using this solution at scale require additional considerations that are not covered in this deployment:
- Multi-Region MediaConvert Endpoint
- We recommend that you use MediaConvert in the same Region that the S3 media bucket is located for lower latency, and save on data transfer cost between Regions.
- Duplicate requests from different Edge locations
- Amazon CloudFront forwards requests from global edge locations to your origin. There may be cases where your viewers send simultaneous requests for the same content rendition. It is recommended to keep a state of the conversion in progress, so only one MediaConvert job is invoked for each content rendition.
- Consider using CloudFront Origin Shield to consolidate origin requests for viewers in different geographical Region.
- Consider using a DynamoDB table to store current conversions-in-progress state. Use the data to determine if a job is in progress, and whether to invoke MediaConvert job or not.
- Amazon CloudFront forwards requests from global edge locations to your origin. There may be cases where your viewers send simultaneous requests for the same content rendition. It is recommended to keep a state of the conversion in progress, so only one MediaConvert job is invoked for each content rendition.
- Update your main manifest with the newly created rendition.
- Your MediaConvert job generates a new main manifest and playlist (child) manifest with every conversion, and stores it in your HLS bucket. If you want to include multiple renditions in the main manifest, you should consider rewriting the main manifest. You can use an S3 event to trigger a Lambda function every time a conversion is completed. Your Lambda function appends the new rendition to a separate main manifest that you can serve Adaptive Bitrate (ABR). Alternatively, if you want to generate multiple renditions at once, you can use MediaConvert Auto-generate ABR package .
Cleanup
To clean up the artifacts from the solution in this post, first delete all files in your Amazon S3 buckets created in this deployment:
- yourHlsStreamBucket-<yourAWSAccountId>
- yourMediaSourceBucket-<yourAWSAccountId>
Then delete the CloudFormation stack that was provisioned at the start of this process in order to clean up all remaining infrastructure.
If the Stack deletion fails, rerun the Stack deletion. This time, CloudFormation opens a dialog, with the option to delete or retain the Lambda@Edge function resource, select Delete.
Conclusion
In this blog, you have learned how to deploy a solution for on-the-fly video conversion. You can apply this workflow to convert your mp4 video source files to HLS video streams in different resolutions. MediaConvert supports other conversion and transformation capabilities, like clipping, cropping, adding quality filters (deblock/denoise). You can use the same on-the-fly workflow to invoke MediaConvert jobs, and apply different conversions as your business require. For more information about MediaConvert conversion capabilities, and API usage refer to MediaConvert documentation.