Watermarking Videos with AWS Elastic Transcoder S3 and Lambda

By Adam McQuistan in Python  05/29/2020 Comment


In this article I walk through the process of setting up an automated process utilizing AWS S3, Lambda, and Elastic Transcoder to embed a watermark in to videos initiated by uploading to an S3 bucket.


1) Create IAM Role and Policy for Lambda

My goal here is to use a Lambda function to respond to S3 events when a video is uploaded then have the function configure and submit an Elastic Transcoder job to embed a watermark image in the video. In order to do this the Lambda function will need privileged access to S3 and Elastic Transcoder which will be accomplished by using a role with a custom policy attached to it.

Once logged into the AWS Console and selecting a region to work in I navigate to the IAM section, click on the Policies link in the left menu then the Create policy button at the top of the screen.

IAM Policy Listing

In the policy creation screen I switch to the JSON editor and type in the following policy allowing access to Elastic Transcoder and S3.

  "Version": "2012-10-17",
  "Statement": [{
      "Effect": "Allow",
      "Action": [
      "Resource": "arn:aws:logs:*:*:*"
      "Action": [
      "Effect": "Allow",
      "Resource": "*"

Next I click the Review button, give the new Policy a name of LambdaS3ElasticTranscoder and, click Create policy button.

Now its time to create a Role and attached the New Policy to it by clicking the Role section in the left menu then, selecting the AWS Services tab on the top along with the Lambda service in the middle of the page before advancing with the Next button.

The resulting page is the Attach Policy section where I search and select the newly created LambdaS3ElasticTranscoder policy.

Advance to the review page and give it a name of LambdaS3ElasticTranscoder then click Create role.

2) Create S3 Input and Output Buckets

Elastic Transcoder executes in what are known as pipelines which are configured to retrieve input media content from an S3 bucket, apply a transcoding solution to it then place the result in an output S3 bucket. In this section I create two such buckets.

To start I navigate to the S3 section and click create bucket.

Next I name the bucket video-transcoding-inputs and advance through the following screens accepting the defaults until the bucket is created.

Then I do the same with another bucket named video-transcoding-outputs.

3) Create Elastic Transcoder Pipeline

Next on the list is to navigate to the Elastic Transcoder home page and click create pipeline.

In the resulting settings page I name the pipeline watermark-demo, select the input bucket of video-transcoding-inputs then select both the video-transcoding-outputs bucket and Standard storage class for outputs then scroll to the bottom and click Create pipeline button.

After creating the watermark-demo pipeline the Elastic Transcoding dashboard updates to now display a table with the newly created pipeline and a menu listing Pipelines, Jobs and, Presets on the left.


4) Create Elastic Transcoder Preset

From the left menu in the Elastic Transcoding dashboard I select Presets then click the Create New Presets button.

In the resulting settings page I select "System preset: Generic 1080p" as a starting point then update the name to be Generic-1080p-Watermark. Afterwards, down in the Available Settings section I deselect Audio as I won't be needing it.

The video settings section can remain unchanged so, I scroll down to the Watermarks section next where I remove all the sections except the BottomLeft watermark Id. Then I update the Max Width and Max Height to 20% and set the Sizing Policy to Fit. These settings mean to set the watermark image to either 20% of the content's width or height which ever is reached first without exceeding the other.

Next I update the Horizontal and Vertical offsets to the value of 4% then set the Opacity to 50% before finally clicking Create preset button.

5) Create Lambda Function and Attach S3 Trigger

Now I can create the Lambda function which will be triggered when a video is uploaded to the inputs S3 bucket. To start I naviate to the Lambda section of the console and click the Create function button.

In the resulting page I enter a name of WatermarkFunction and select the most recent Python runtime (Python 3.7 as of this writing).

Next I scroll down to the execution role section and select "Use an existing role" and select the LambdaS3ElasticTranscoder role I made initially before clicking the Create function button.

The following page has a Design section at the top. In this section I click the add trigger button to add the S3 trigger to initiate the execution of the Lambda function.

On the Create Trigger page I select S3 as the source then select the video-transcoding-inputs bucket and limit the Suffix filter to .mp4 files and click Add.

Back in the Lambda Design section I scroll down to the Function code editor and insert the below code.

import os

from urllib import parse as urlparse
import boto3

def lambda_handler(event, context):
    transcoder = boto3.client('elastictranscoder', region_name='us-east-1')
    pipeline_id = os.environ['PIPELINE_ID']
    preset_id = os.environ['PRESET_ID']
    watermark_id = os.environ['WATERMARK_ID']
    watermark_key = os.environ['WATERMARK_KEY']
    key = urlparse.unquote_plus(
    input_options = {
        'Key': key
    output_options = {
        'Key': key,
        'PresetId': preset_id,
        'Watermarks': [{
            'PresetWatermarkId': watermark_id,
            'InputKey': watermark_key
    job_response = transcoder.create_job(
    print(f"\nJobId={job_response['Job']['Id']} status={job_response['Job']['Status']}")
    print(f"  Input={key}")
    print(f"  Output={key}")
    print(f"  Watermark={watermark_key}")
    print(f"  Submitted={job_response['Job']['Timing']['SubmitTimeMillis']}")

The Python code used in the Lambda function instantiates the boto3 transcoder client in the same region I've been working in. Then it uses the os module to access a few environment variables (to be added soon) corresponding to the pipeline, preset settings, and the watermark image to be used. Following that the code parses out the S3 object key for the video file being uploaded initiating the trigger and, use that to construct an input_options dictionary variable. Next the output_options dictionary is created to specify the preset and watermark to be used in the Elastic Transcoder pipeline.

These settings dictionaries are used along with the pipeline Id to submit a job using the boto3 transcoding client object before printing out the information in the returned job response.

The last thing to do is to scroll down to the environment variables section and populate it with the PIPELINE_ID, PRESET_ID, WATERMARK_ID, and WATERMARK_KEY used in the Python function.

Lastly, click the Save button at the of the page to finalize creating the Lambda function.

6) Upload Video to S3 and Enjoy the Automation

The final piece is to upload an image to be used for watermarking, tci-logo.png in my example, and an input MP4 video which will initiate the process of transcoding the video to include the image.

Once uploading the image then the video (in that order) I can head over to the Lambda section again, click on the newly created function, and select the monitoring tab.

Notice the Invocations chart on the top left shows that its been invocated once.

I can now go over to the output S3 bucket, download the transcoded video, and view it to ensure it contains the watermark image.



Resources For Learning More

thecodinginterface.com earns commision from sales of linked products such as the books above. This enables providing continued free tutorials and content so, thank you for supporting the authors of these resources as well as thecodinginterface.com


In this article I've demonstrated how to use the AWS S3, Lambda, and Elastic Transcoder services to embed a watermark into videos all in a seamless automated workflow.

As always, thanks for reading and don't be shy about commenting or critiquing below.

Share with friends and colleagues

[[ likes ]] likes

Community favorites for Python