Implementation: MediaConvert
To transcode large video files that a serverless code function can't handle, you need to use a dedicated transcoding service. This page demonstrates how to do this with AWS.
Transcoding large video files on AWS
There are three main services we'll be making use of here:
- AWS Elemental MediaConvert: the service that will actually perform the video transcoding
- AWS EventBridge: the service that will notify us when a transcoding job is completed, so we can automatically update our databases or whatever else we want to do.
- AWS Lambda: to receive notifications from EventBridge, and update our database with the newly transcoded video URLs.
Technically, you don't need Lambda and EventBridge here if you wanted to run a few videos through MediaConvert manually. However, unlike say the Lambda API, you can't schedule a MediaConvert job and then wait to get a response, so we are using Lambda here to know when a job finishes and do something with the response from that job.
Invoking MediaConvert via the AWS SDK
You'll need to set up the following resources before you can try to run the code below:
- An IAM role for MediaConvert to use (you can do this in the Create Job area in the MediaConvert console, follow the directions)
- An IAM user with the above role, with an access and secret key pair to use for authentication
Creating an Output Preset and Job Template
To make running jobs on MediaConvert easier, you can define templates and presets that you want to use often, and pass them as parameters when creating jobs, so you don't have to specify them every time.
Example Output Preset
This is the output preset in JSON form; you can configure an output preset in the MediaConvert console, and then export/import them as JSON files like this.
This particular preset specifies that the output file should be scaled to 1920x1080, fitting the video to that resolution without cropping it or stretching it. It specifies x264/AVC as the encoder to use, the QVBR quality level and maximum bitrate, among other parameters.
Note that the QualityTuningLevel parameter has a major impact on the price of transcoding on MediaConvert, as does the codec you choose. MediaConvert's pricing is split into two tiers, Basic and Pro; x265/HEVC is only available on Pro which is more expensive, and multi-pass or single-pass with high quality are even more expensive on Pro, as opposed to single pass optimised for speed.
{
"Description": "For 1080p video transcoding.",
"Name": "Deserted-Chateau-Standard-1080p",
"Settings": {
"VideoDescription": {
"Width": 1920,
"ScalingBehavior": "FIT_NO_UPSCALE",
"Height": 1080,
"CodecSettings": {
"Codec": "H_264",
"H264Settings": {
"MaxBitrate": 6000000,
"RateControlMode": "QVBR",
"QvbrSettings": {
"QvbrQualityLevel": 9
},
"SceneChangeDetect": "TRANSITION_DETECTION",
"QualityTuningLevel": "SINGLE_PASS_HQ"
}
}
},
"AudioDescriptions": [
{
"CodecSettings": {
"Codec": "AAC",
"AacSettings": {
"Bitrate": 96000,
"CodingMode": "CODING_MODE_2_0",
"SampleRate": 48000
}
}
}
],
"ContainerSettings": {
"Container": "MP4",
"Mp4Settings": {}
}
}
}
Understanding the output preset parameters
Some of the output preset parameters are very significant in terms of the final output and the transcoding cost. A basic rundown is below.
Parameter |
Explanation |
ScalingBehavior | How the video will be resized to meet the dimensions you specify. FIT_NO_UPSCALE is the most appropriate option in most scenarios; the default will stretch videos that aren't in the exact resolution or aspect ratio you specify. |
QVBR Quality Level | QVBR is AWS' weird unique take on VBR (variable bitrate encoding). The quality level you set here has a big impact on the final quality of the output video. |
Maximum bitrate | As QVBR is variable in bitrate, this specifies the absolute maximum bitrate any part of the video can reach. The lower you set this, the lower the maximum video quality can be. |
Example Job Template
This is a Job Template in JSON form. You can specify an output preset to use for the template, which makes managing a job template a lot easier than having all the output details in the template itself.
The main point of the job template is to specify where the result files of the transcoding, i.e. the newly transcoded videos, should be uploaded. You can also specify how often MediaConvert should update the status of the job, though this isn't relevant most of the time.
Note the the AccelerationSettings parameter only works on the Pro tier, and is only used when a job requires a sufficiently large amount of processing power. If you're using the Pro tier, you can set this to preferred and it'll automatically use acceleration when eligible (it doesn't cost any extra). This reduces the time required to complete a given transcoding job.
{
"Name": "<the name you want your Job Template to have>",
"Settings": {
"TimecodeConfig": {
"Source": "ZEROBASED"
},
"OutputGroups": [
{
"Name": "File Group",
"Outputs": [
{
"Preset": "<the name of your Output Preset>"
}
],
"OutputGroupSettings": {
"Type": "FILE_GROUP_SETTINGS",
"FileGroupSettings": {
"Destination": "s3://<your S3 bucket name>/<some path>/",
"DestinationSettings": {
"S3Settings": {
"StorageClass": "<the S3 storage class to use, e.g. INTELLIGENT_TIERING>"
}
}
}
}
}
],
"Inputs": [
{
"AudioSelectors": {
"Audio Selector 1": {
"DefaultSelection": "DEFAULT"
}
},
"VideoSelector": {},
"TimecodeSource": "ZEROBASED"
}
]
},
"AccelerationSettings": {
"Mode": "PREFERRED"
},
"StatusUpdateInterval": "SECONDS_60",
"Priority": 0,
"HopDestinations": []
}
Example code to transcode a video using the Job Template
Here's an example of how to start a transcoding job, in Node.js, using the Job Template and Output Preset you created earlier.
import { CreateJobCommand, MediaConvertClient } from "@aws-sdk/client-mediaconvert";
let mediaConvertClient = new MediaConvertClient({
region: "<the AWS region you want to use>",
/*
The endpoint is a unique URL for your account, per region. You can
find it in the MediaConvert "account" tab.
*/
endpoint: "<your MediaConvert endpoint for the region>",
credentials: {
accessKeyId: "<your user's IAM access key>",
secretAccessKey: "<your user's IAM secret key>",
},
});
let params = {
JobTemplate: "<the name of the JobTemplate> to use",
Role: "arn:aws:iam::<your AWS account number>:role/service-role/<your IAM role for using MediaConvert>",
Settings: {
Inputs: [
{
FileInput: "<the S3 URL to the video you want to transcode>",
},
],
},
};
let command = new CreateJobCommand(params);
let response = await mediaConvertClient.send(command);
The response here is just an acknowledgment of whether the job command was started or not, as opposed to returning the actual result of the job (as for longer jobs, it could take a pretty long time). An error will be returned if the job command didn't work, because of invalid credentials or parameters, but not if the job starts and fails for some other reason.