├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .vscode
└── settings.json
├── 1-IAMandS3
├── MediaConvertIAMUser.yaml
├── MediaConvertIAMandS3.yaml
├── README-cf.md
├── README-user.md
└── README.md
├── 10-TestingHLS
└── README.md
├── 11-VODMediaTailor
├── CloudFrontBehaviorSettings.png
├── CloudFrontOriginSettings.png
├── MediaTailorCDNSettings.png
├── README.md
├── emt-vod-workflow.png
└── emt_config.png
├── 12-AdMarkerInsertion
├── README.md
├── emc-sample-manifest-conditioning.xml
└── emc-sample-signal-notification.xml
├── 13-VODMediaPackage
├── README-tutorial.md
├── README.md
└── ingest_asset.py
├── 2-MediaConvertJobs
├── ExportImportJob.md
├── MediaConvertJobs.yaml
└── README.md
├── 3-Inputs
└── README.md
├── 4-Outputs
└── README.md
├── 5-Captions
├── CAPTIONS_en.srt
├── CAPTIONS_ru.srt
├── README.md
└── TranscribeCaptions.md
├── 6-EmbeddedMetadata
└── README.md
├── 7-MediaConvertJobLambda
├── AutomatingWithEDL.md
├── README.md
├── WatchFolder.yaml
├── convert.py
├── input_clip.zip
├── input_clip_convert.py
├── input_clipping_job.json
├── job.json
├── lambda.zip
├── llama-ad-llama.edl
├── test.mp4
└── ziplambda.sh
├── 8-MediaConvertEvents
├── .gitignore
├── README-cloudformation.md
├── README-tutorial.md
├── simple-events.yaml
└── simple_event_collector.py
├── 9-MediainfoLambda
├── .gitignore
├── README-tutorial.md
├── mediainfo.py
├── mediainfo.yaml
└── requirements.txt
├── LICENSE
├── NOTICE
├── README.md
├── VODConsoleWorkshop.yaml
├── Workflow-WatchfolderAndNotification
├── .gitignore
├── README-tutorial.md
├── README.md
├── WatchFolder.yaml
├── convert.py
├── job.json
└── test.mp4
└── images
├── HLS.png
├── Kibana.png
├── MediaConvertBasePipeline.png
├── ProgressMetricsStack.png
├── ProgressStack.png
├── S3-access-lambda.png
├── WatchfolderSlide.png
├── WorkloadMonitoringStack.png
├── beaches-channel-captions.png
├── burnin-captions-settings.png
├── cf-outputs-role.png
├── cf-simple-events.png
├── cf-watchfolder-stack.png
├── cloudformation-outputs.png
├── cloudwatch-rule-allevents.png
├── configure-test-event.png
├── consoleUserMetadata.png
├── create-lambda-convert.png
├── create-source-bucket.png
├── custom-vocabulary.png
├── cw-event-rule.png
├── cw-input-rule.png
├── dynamoTTL.png
├── dynamotable.png
├── edl-mediaconvert-basic-info.png
├── edl-mediaconvert-function-code.png
├── edl-mediaconvert-job-inputs.png
├── edl-mediaconvert-job.png
├── edl-mediaconvert-s3-trigger.png
├── edl-mediaconvert-trigger-config.png
├── emc-explained-output.png
├── emp-vod-asset-details.png
├── emp-vod-asset.png
├── emp-vod-cw-event-rule.png
├── emp-vod-ingest-asset.png
├── emp-vod-lambda-env-vars.png
├── emp-vod-packaging-group.png
├── emp-vod-playback-details.png
├── emp-vod-playback-preview.png
├── emp-vod-role-lambda.png
├── esam-settings.png
├── eventlambdarole.png
├── hlsjs-metrics.png
├── hlsjs-quality-levels.png
├── hlsjs-vanlife-hls.png
├── iam-role-mediaconvert.png
├── input-captions-settings.png
├── kibana-dashboard.png
├── kibana-saved-objects.png
├── kibana-timepicker.png
├── lambda-convert-author.png
├── lambda-environment.png
├── lambda-function-code-wfnotification.png
├── lambda-function-code.png
├── lambda-mediainfo-code.png
├── lambda-mediainfo.png
├── lambda-s3-trigger.png
├── lambda-simeple-event-test.png
├── lambda-simple-event-code.png
├── lambda-simple-event-collector.png
├── lambda-simple-event-env.png
├── lambda-simple-events-designer.png
├── lambda-trigger-confgure.png
├── media-convert-start-0.png
├── mediaconvert-burnin.png
├── mediaconvert-captions-burn-in-mp4.png
├── mediaconvert-captions-burnin-job.png
├── mediaconvert-captions-selector.png
├── mediaconvert-captions.png
├── mediaconvert-clip.png
├── mediaconvert-create-job-input1.png
├── mediaconvert-frame-capture.png
├── mediaconvert-hls-3-outputs.png
├── mediaconvert-hls-settings.png
├── mediaconvert-input-clipping.png
├── mediaconvert-job-Q.png
├── mediaconvert-job-admarker-Q.png
├── mediaconvert-job-admarker.png
├── mediaconvert-job-burnin.png
├── mediaconvert-job-captions-Q.png
├── mediaconvert-job-captions.png
├── mediaconvert-job-clip-stitch-Q.png
├── mediaconvert-job-clip-stitch.png
├── mediaconvert-job-mt2ts.png
├── mediaconvert-job-settings.png
├── mediaconvert-job-summary.png
├── mediaconvert-job-timecode-burnin-Q.png
├── mediaconvert-job.png
├── mediaconvert-json-output-groups.png
├── mediaconvert-mp4-output-group.png
├── mediaconvert-save-jobid.png
├── mediaconvert-start-0.png
├── mediaconvert-stitch.png
├── mediaconvert-timecode-burn-in.png
├── mediaconvert-timecode-watermark.png
├── mediaconvert-watermark.png
├── mediainfo-dynamodb.png
├── mediainfo-lambda-role.png
├── mediainfo.png
├── mediatailor-configuration.png
├── module-2-fin.png
├── module-2-s3-Q.png
├── module-2-s3-link.png
├── module-2-s3.png
├── module-3-job-fin-Q.png
├── module-3-job-fin.png
├── module-4-hls-timecode.png
├── module-4-mp4-watermark.png
├── module-5-fin.png
├── module-6-fin.png
├── play-test-video.png
├── role-link.png
├── s3-create-watchfolder.png
├── segment-settings.png
├── simple-events.png
├── simple-watchfolder.png
├── sns-create-topic.png
├── sns-email.png
├── sns-subsciption.png
├── test-mediainfo.png
├── transcribe-job.png
├── verify-watchfolder.png
├── watchfolder.png
└── watchfoldere2e.png
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | push.sh
3 | 8-MediaConvertEventLambda
4 | mediaconvert
5 | mediaconvert/*
6 |
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.pythonPath": "${workspaceFolder}/mediaconvert/bin/python"
3 | }
--------------------------------------------------------------------------------
/1-IAMandS3/MediaConvertIAMUser.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates IAM user permissions needed to complete the VOD
6 | MediaConvert Workshop. S3 permissions are full access in
7 | this sample, but can be restricted to only the input/output
8 | buckets, if desired.
9 |
10 | Resources:
11 |
12 | MediaConvertUserPolicy:
13 | Type: "AWS::IAM::ManagedPolicy"
14 | Properties:
15 | ManagedPolicyName: !Sub "${AWS::StackName}-MediaConvertUsersPolicy"
16 | PolicyDocument:
17 | Version: "2012-10-17"
18 | Statement:
19 | -
20 | Effect: "Allow"
21 | Action:
22 | - "mediaconvert:*"
23 | Resource: "arn:aws:mediaconvert:*"
24 | -
25 | Effect: "Allow"
26 | Action:
27 | - "iam:ListRoles"
28 | - "iam:PassRole"
29 | Resource: "arn:aws:iam::*"
30 | -
31 | Effect: "Allow"
32 | Action:
33 | - "s3:*"
34 | Resource: "*"
35 | -
36 | Effect: "Allow"
37 | Action:
38 | - cloudformation:Describe*
39 | - cloudformation:EstimateTemplateCost
40 | - cloudformation:Get*
41 | - cloudformation:List*
42 | - cloudformation:ValidateTemplate
43 | Resource: "*"
44 |
45 | Outputs:
46 | MediaConvertUserPolicyArn:
47 | Value: !Ref MediaConvertUserPolicy
48 |
--------------------------------------------------------------------------------
/1-IAMandS3/MediaConvertIAMandS3.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates IAM role needed to complete the VOD
6 | MediaConvert Workshop. S3 permissions are full access in
7 | this sample, but can be restricted to only the input/output
8 | buckets, if desired.
9 |
10 | Creates a bucket to store inputs and outputs for MediaConvert and a role to
11 | pass to MediaConvert to access the bucket and other account resources
12 | MediaConvert needs to process jobs from the console and API. For the lab,
13 | the bucket is website enabled so that output videos can play out as web resources.
14 |
15 | #Parameters:
16 | #MediaBucket:
17 | # Type: String
18 | # Description:
19 | # The name for the bucket you want to use for MediaConvert
20 | # inputs and ouputs, e.g. 'vod-yourname.'
21 |
22 | Resources:
23 | MediaConvertRole:
24 | Type: AWS::IAM::Role
25 | Properties:
26 | RoleName: !Sub "${AWS::StackName}-MediaConvertRole"
27 | AssumeRolePolicyDocument:
28 | Version: 2012-10-17
29 | Statement:
30 | -
31 | Effect: Allow
32 | Principal:
33 | Service:
34 | - "mediaconvert.amazonaws.com"
35 | - "mediaconvert.us-east-1.amazonaws.com"
36 | - "mediaconvert.ap-northeast-1.amazonaws.com"
37 | - "mediaconvert.ap-southeast-1.amazonaws.com"
38 | - "mediaconvert.ap-southeast-2.amazonaws.com"
39 | - "mediaconvert.eu-central-1.amazonaws.com"
40 | - "mediaconvert.eu-west-1.amazonaws.com"
41 | - "mediaconvert.us-east-1.amazonaws.com"
42 | - "mediaconvert.us-west-1.amazonaws.com"
43 | - "mediaconvert.us-west-2.amazonaws.com"
44 | Action:
45 | - sts:AssumeRole
46 | Policies:
47 | -
48 | PolicyName: !Sub "${AWS::StackName}-MediaConvertPolicy"
49 | PolicyDocument:
50 | Statement:
51 | -
52 | Effect: "Allow"
53 | Action:
54 | - "s3:*"
55 | Resource:
56 | - "*"
57 | -
58 | Effect: "Allow"
59 | Action:
60 | - "autoscaling:Describe*"
61 | - "cloudwatch:*"
62 | - "logs:*"
63 | - "sns:*"
64 | Resource:
65 | - "*"
66 |
67 | MediaBucket:
68 | Properties:
69 | WebsiteConfiguration:
70 | IndexDocument: index.html
71 | CorsConfiguration:
72 | CorsRules:
73 | - AllowedHeaders: ['*']
74 | AllowedMethods: [GET]
75 | AllowedOrigins: ['*']
76 | ExposedHeaders: [Date]
77 | Id: myCORSRuleId1
78 | MaxAge: '3600'
79 | LifecycleConfiguration:
80 | Rules:
81 | - Id: ExpireRule
82 | Status: Enabled
83 | ExpirationInDays: '7'
84 | DeletionPolicy: Retain
85 | Type: "AWS::S3::Bucket"
86 |
87 | MediaBucketPolicy:
88 | Properties:
89 | Bucket: !Ref MediaBucket
90 | PolicyDocument:
91 | Version: 2012-10-17
92 | Statement:
93 | -
94 | Effect: Allow
95 | Principal: "*"
96 | Action: s3:GetObject
97 | Resource: !Sub "arn:aws:s3:::${MediaBucket}/*"
98 | Type: "AWS::S3::BucketPolicy"
99 |
100 | Outputs:
101 | MediaBucket:
102 | Value: !Ref MediaBucket
103 | MediaConvertRole:
104 | Value: !Ref MediaConvertRole
105 | MediaConvertRoleArn:
106 | Value: !GetAtt MediaConvertRole.Arn
107 |
--------------------------------------------------------------------------------
/1-IAMandS3/README-cf.md:
--------------------------------------------------------------------------------
1 | ### (Optional) Instructions for running CloudFormation
2 |
3 | 1. Make sure your region is set to US-West-Oregon for this lab.
4 | 2. From the AWS Management Console, click on **Services** and then select **CloudFormation**.
5 | 3. Select **Create stack** to go to the **Create stack** page
6 | 4. Select the **Upload a template to Amazon S3** checkbox then select **Choose file**
7 | 5. Navigate to the directory where you downloaded the lab. Then select **1-IAMandS3->MediaConvertIAMandS3.yaml**. Then select **Open**.
8 | 6. Select **Next** to move to the **Specify details** page.
9 | 7. Enter `vod` for the in the **Stack name** box. Note: you can choose other stack names, but using "vod" will create resource with names consistent with the rest of the lab.
10 | 8. Select **Next** to move to the **Options** page. Leave this page as defaults.
11 | 9. Select **Next** to move to the **Review** page.
12 | 10. Select the checkbox to acknowledge creating resources, then select **Create**
13 | 11. Wait for the stack to be created.
14 | 12. From the Stacks page, find the Stack called **vod**.
15 | 13. Go to the Stack details page and expand the Outputs section of the page. You will find two outputs there:
16 |
17 | * **MediaConvertRole** is the ARN for the AWS Role that can be passed to MediaConvert to grant access to S3 and other account resources MediaConvert needs to process jobs.
18 | 14. Save this page in a browser tab or save the ARNs to be used in future steps of the Workshop.
19 |
20 | Move forward to the next module [**AWS Elemental Media Convert Jobs**](../2-MediaConvertJobs/README.md).
--------------------------------------------------------------------------------
/1-IAMandS3/README-user.md:
--------------------------------------------------------------------------------
1 | #### High-Level Instructions
2 |
3 | You may want to create restricted users to work with MediaConvert. This section goes through creating the policy needed to complete this lab for a user that doesn't have Administrator access. This step needs to be completed by a user with Administrator access to grant permissions.
4 |
5 | Create an IAM Policy and name it `vod-MediaConvertUserPolicy`. Use inline policies to grant permissions to other resources needed for the execute MediaConvert. Attach the new policy to an IAM user.
6 |
7 | #### Detailed Instructions - create a managed policy
8 | 1. From the AWS Management Console, click on **Services** and then select **IAM** in the Security, Identity & Compliance section.
9 | 1. Select **Policies** from the side bar menu.
10 | 1. Click on the **Create policy** button.
11 | 1. Select **Create Your Own Policy**.
12 | 1. Enter `vod-MediaConvertUserPolicy` as the policy name
13 | 1. Copy and paste the following JSON into the **Policy Document**, then select **Create** to create the policy
14 | ```
15 | {
16 | "Version": "2012-10-17",
17 | "Statement": [
18 | {
19 | "Sid": "AccessMediaConvert",
20 | "Effect": "Allow",
21 | "Action": [
22 | "mediaconvert:*"
23 | ],
24 | "Resource": [
25 | "arn:aws:mediaconvert:*"
26 | ]
27 | },
28 | {
29 | "Sid": "PassRolestoMediaConvert",
30 | "Action": [
31 | "iam:ListRoles",
32 | "iam:PassRole"
33 | ],
34 | "Effect": "Allow",
35 | "Resource": "arn:aws:iam::*"
36 | },
37 | {
38 | "Sid": "ListWriteS3Buckets",
39 | "Action": "s3:*",
40 | "Effect": "Allow",
41 | "Resource": "*"
42 | }
43 | ]
44 | }
45 | ```
46 |
47 | #### Detailed Instructions - attach the managed policy to an IAM user
48 |
49 | 1. Select **Users** from the IAM side bar menu.
50 | 1. Click on the User Name you want to add permissions to navigate to the user Summary page.
51 | 1. Select **Add permissions**
52 | 1. On the Grant Permissions page, select **Attach existing policies directly**
53 | 1. Enter `vod-MediaConvertUserPolicy` in the search box and then select the checkbox for the policy from the returned results.
54 | 1. Select the **Next: Review** button on the bottom of the page.
55 | 1. Select the **Add permission** button
56 |
57 | 6. Click on Validate Policy to check for typos, then click Apply Policy
--------------------------------------------------------------------------------
/1-IAMandS3/README.md:
--------------------------------------------------------------------------------
1 | # AWS IAM and S3 User Module
2 |
3 | This module guides the participant in setting up the AWS resources needed to complete the Video on Demand workshop. You will create and configure an AWS S3 bucket to store outputs from MediaConvert. You will also create a role that allows MediaConvert access to the resources in your account that are needed to convert videos.
4 |
5 | You can optionally create a restricted user with access only to the resources needed to complete the lab.
6 |
7 | ## Prerequisites
8 |
9 | ### Region
10 |
11 | MediaConvert is available in several regions. But for the purpose of this lab, we will use the **US West (Oregon)** region.
12 |
13 | In order to complete this workshop you'll need an AWS Account with access to create policies and roles within the AWS Identity and Access Management (IAM) service.
14 |
15 | The signed-in user must have the AdministratorAccess policy or a policy that allows the user to access all actions for the mediaconvert service and at least read access to CloudWatch. The steps for creating a policy for AWS Elemental MediaPConvert is covered near the end of this module.
16 |
17 | The code and instructions in this workshop assume only one student is using a given AWS account at a time. If you try sharing an account with another student, you'll run into naming conflicts for certain resources. You can work around this by either using a suffix in your resource names or using distinct Regions, but the instructions do not provide details on the changes required to make this work.
18 |
19 | ### (Optional) Skip ahead with CloudFormation
20 |
21 | If you would like to skip this part of the lab and move on the the next module, a CloudFormation script is provided in this folder in `MediaConvertIAMandS3.yaml`.
22 |
23 | [**Instructions to create resources in this section using CloudFormation**](README-cf.md)
24 |
25 | Otherwise, continue to the next section.
26 |
27 | ## 1. Create an IAM Role to Use with AWS Elemental MediaConvert
28 |
29 | MediaConvert will need to be granted permissions to read and write files from your S3 buckets and generate CloudWatch events as it processes videos. MediaConvert is granted the permissions it needs by assuming a role that is passed to it when you create a job.
30 |
31 | #### High-Level Instructions
32 |
33 | Use the IAM console to create a new role. Name it `vod-MediaConvertRole` and select AWS MediaConvert.
34 |
35 | #### Detailed Instructions
36 |
37 | 1. From the AWS Management Console, click on **Services** and then select **IAM** in the Security, Identity & Compliance section.
38 |
39 | 1. Select **Roles** in the left navigation bar and then choose **Create role**.
40 |
41 | 1. Select **AWS Service** and **MediaConvert** for the role type, then click on the **Next:Permissions** button.
42 |
43 | **Note:** Selecting a role type automatically creates a trust policy for your role that allows AWS services to assume this role on your behalf. If you were creating this role using the CLI, AWS CloudFormation or another mechanism, you would specify a trust policy directly.
44 |
45 | 1. Click on **Next:Tags**.
46 |
47 | 1. Click on **Next:Review**.
48 |
49 | 1. Enter `vod-MediaConvertRole` for the **Role name**.
50 |
51 | 1. Choose **Create role**.
52 |
53 | 1. Copy and save off the Role ARN of the role you just created using your favorite editor. You will be needing this in the subsequent modules.
54 |
55 | ## 2. Create an S3 bucket to store and host MediaConvert outputs
56 |
57 | In this section, you will use the AWS console to create an S3 bucket to store video and image outputs from MediaConvert and host a simple web page that can be used to play out the videos. Later, the resulting videos and images will be played out using the S3 https resource using several different players both inside and outside of the the amazonaws domain.
58 |
59 | In order to facilitate https access from anonymous sources inside and outside the amazonaws domain, such as video players on the internet, you will add the following settings to the S3 bucket:
60 |
61 | * a bucket policy that enables public read
62 | * a policy for Cross Origin Resource Sharing (CORS)
63 |
64 | #### Detailed instructions
65 |
66 | 1. In the AWS Management Console choose **Services** then select **S3** under Storage.
67 |
68 | 1. Choose **+Create Bucket**.
69 |
70 | 1. Provide a globally unique name for your bucket such as `vod-lastname`.
71 |
72 | 1. Select the Region you've chosen to use for this workshop from the dropdown.
73 |
74 | 1. Choose **Create** in the lower left of the dialog without selecting a bucket to copy settings from.
75 |
76 | 1. From the S3 console select the bucket you just created and go to the Overview page.
77 | 1. Select the **Properties** tab and click on the **Static website hosting** tile.
78 | 1. Select the **Use this bucket to host a website** box.
79 | 1. Enter `index.html` in the **Index document** box.
80 | 1. Select **Save**.
81 | 1. Select the **Permissions** tab.
82 | 1. Under **Block public access**, click on the **Edit** button.
83 | 1. Uncheck **Block all public access** and click the **Save** button.
84 | 1. Type `confirm` in the textbox that pops up and click **Confirm**.
85 | 1. Select **Bucket policy** and paste the following JSON into the bucket policy editor.
86 | 1. Replace the text **YOUR-BUCKETNAME** with the name of the bucket you created earlier in this module.
87 |
88 | ```
89 | {
90 | "Version": "2012-10-17",
91 | "Statement": [
92 | {
93 | "Sid": "AddPerm",
94 | "Effect": "Allow",
95 | "Principal": "*",
96 | "Action": "s3:GetObject",
97 | "Resource": "arn:aws:s3:::YOUR-BUCKETNAME/*"
98 | }
99 | ]
100 | }
101 | ```
102 | 1. Click on **Save**
103 | 1. Next, click on **CORS configuration** and enter the following XML into the **CORS configuration editor**.
104 | ```
105 |
106 |
107 |
108 | *
109 | GET
110 | 3000
111 | *
112 |
113 |
114 | ```
115 |
116 | 1. Select **Save**
117 |
118 | ## (Optional) Adding AWS Elemental MediaConvert permissions to an IAM user
119 |
120 | You may want to create restricted users to work with MediaConvert. This section goes through creating the policy needed to complete this lab for a user that doesn't have Administrator access. This step needs to be completed by a user with Administrator access to grant permissions.
121 |
122 | [**Instructions to create a restricted user**](README-user.md)
123 |
124 | ## Completion
125 |
126 | At the end of the module you have created a IAM Role to allow access from MediaConvert to resources in your account. You have also (optionally ) added MediaConvert permissions to a user.
127 |
128 | Move forward to the next module [**AWS Elemental Media Convert Jobs**](../2-MediaConvertJobs/README.md).
129 |
--------------------------------------------------------------------------------
/10-TestingHLS/README.md:
--------------------------------------------------------------------------------
1 | # Video players
2 |
3 | If you completed the Live Streaming Workshop, then this content will be somewhat familiar since it overlaps in the discussion of tools for playing HLS video from https endpoints. In addition to HLS players, we will go a little deeper into observing the components of the video packages produced by MediaConvert including more details about how adaptive bitrate works and use the hls.js demo page to observe how a player plays out our HLS video.
4 |
5 | ## Prerequisites
6 |
7 | ### Previous Modules
8 |
9 | This module relies on the configuration of IAM and S3 done in modules 1 and 2.
10 |
11 | You need the following resources created in modules 1 and 2:
12 | * **MediaConvert outputs from module 2 in S3** - you will be playing out the HLS video created from module 2.
13 |
14 | ### Video Players for Testing
15 |
16 | We will use web-based players that can play out videos from https endpoints to test our videos. These players are useful for demonstrating playout of various video features including adaptive bitrate streaming, captioning, and multi-language audio. In all cases, you provide the endpoint URL from S3 to the player.
17 |
18 | 1. https://video-dev.github.io/hls.js/demo/
19 | 2. http://videojs.github.io/videojs-contrib-hls/
20 | 3. https://developer.jwplayer.com/tools/stream-tester/
21 |
22 | ## Test the HLS output from MediaConvert
23 |
24 | HLS stands for HTTP Live Streaming. It is an _adaptive bitrate_ format used for delivering video over the internet. Video is stored as equal _segments_ in multiple files, with the extensions .ts, that make up a _transport stream_. Each transport stream has a _manifest_ file, with the extension .m3u8, that tells the player how to play out files in the transport stream. Each video can also have multiple transport streams of the same video encoded at different bitrates. Since HLS is an adaptive bitrate protocol, the player can select the best quality bitrate based on the network bandwith available at a given time. The player can also switch between bitrates at segment boundaries to adjust to changes in network badwidth. Finally, each group of transport streams has a _package manifest_ file with a .m3u8 extension that stores information about all of the available transport stream for the video.
25 |
26 | The image below shows the hierarchy of files that MediaConvert produces for the HLS output group for the VANLIFE video.
27 |
28 | 
29 |
30 | ### View HLS video quality levels and metrics from a player
31 |
32 | 1. Navigate to the AWS S3 Summary page for the ouput bucket you used in your MediaConvert job.
33 | 1. Open the **assets/HLS** folder.
34 | 2. Locate the package manifest file **VANLIFE.m3u8** and click on the object name to open the Overview page for the object.
35 | 1. Copy the link from the bottom of the page.
36 | 1. Open the hls.js demo page and paste the link into the hls.js player and play the video: https://video-dev.github.io/hls.js/demo/
37 |
38 | 
39 |
40 | 3. Select the **toggle Quality Levels controls** to show the different quality levels in the adaptive bitrate stack. You should see the three levels corresponding to the three MediaConvert outputs in the Apple HLS output group.
41 |
42 | 
43 |
44 | 1. You can select the different quality levels to contol which output to play in the player.
45 | 4. Next, select the **toggle Metrics controls** to show a chart of the bitrate and quality level selection chosen by the player over time.
46 |
47 | 
48 |
49 | ## Completion
50 |
51 | At the end of the module you learned how to test streaming video endpoints using web players. You learned about the design of browser pages that support video playback, how different security models of browsers effect streaming playback, and how to access live and restart window streams of the origin service using URL parameters.
52 |
--------------------------------------------------------------------------------
/11-VODMediaTailor/CloudFrontBehaviorSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/11-VODMediaTailor/CloudFrontBehaviorSettings.png
--------------------------------------------------------------------------------
/11-VODMediaTailor/CloudFrontOriginSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/11-VODMediaTailor/CloudFrontOriginSettings.png
--------------------------------------------------------------------------------
/11-VODMediaTailor/MediaTailorCDNSettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/11-VODMediaTailor/MediaTailorCDNSettings.png
--------------------------------------------------------------------------------
/11-VODMediaTailor/README.md:
--------------------------------------------------------------------------------
1 | # VOD Ad insertion with AWS Elemental MediaTailor
2 |
3 | After you have transcoded video assets into HLS with AWS Elemental MediaConvert, one thing you might want to do is monetize your content. The AWS Elemental MediaTailor service helps you achieve just that. You may learn more about the service [here](https://aws.amazon.com/mediatailor/).
4 |
5 | This workshop will take you through creating an AWS Elemental MediaTailor configuration using a media asset from a storage like AWS Elemental MediaStore, and Ad Decision Server (ADS) that returns a VMAP response. VMAP is one of the ad serving protocols that AWS Elemental MediaTailor supports and lends itself well to VOD workflows. The VMAP protocol defines the ad breaks and their timings, and can be used with assets with no ad markers, which is what we will be using here. Once the basic MediaTailor configuration has been completed and verified, this workshop will also walk you through integrating the configuration with CloudFront for distribution.
6 |
7 | Below is a diagram showing how the media asset interacts with AWS Elemental MediaTailor.
8 | 
9 |
10 | ## Prerequisites
11 | This lab assumes that you have the following:
12 | 1. An HLS media asset either in Amazon S3, AWS Elemental MediaStore, or some other file storage. The asset need not have any ad markers. In this tutorial, we will use [this asset](https://s3-us-west-2.amazonaws.com/vast-demo-bucket/caminandes/master.m3u8) stored in S3.
13 | 1. An Ad Decision Server (ADS), or you may use a static VAST response XML hosted on a server. Here, we will use a sample VMAP response hosted and publicly made available by Google's DoubleClick for Publishers and can be found [here](https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=).
14 |
15 |
16 | ## Implementation Instructions
17 |
18 | ### 1. Create an AWS Elemental MediaTailor Configuration
19 |
20 | **Step-by-step instructions**
21 |
22 | 1. From the AWS Management Console, choose **Services** then select **AWS Elemental MediaTailor**.
23 |
24 | 1. Click on **Create configuration**.
25 |
26 | 1. Enter `MyTestCampaign` for the **Configuration Name**.
27 |
28 | 1. For the **Video content source**, enter the S3 URL link to the asset Endpoint URL `https://s3-us-west-2.amazonaws.com/vast-demo-bucket/caminandes/master.m3u8` but **_without the manifest filename_**. That is, **omit** master.m3u8.
29 |
30 | 1. Enter `https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=[avail.random]` for the **Ad decision server**.
31 |
32 | 
33 |
34 | 1. Click **Create Configuration**. Click on the **Configurations** link to see the configuration you just created. Click on **MyTestCampaign** to see the **Playback endpoints** populated with playback URLs. Note down the **HLS playback prefix** as you'll need it in the next section.
35 |
36 |
37 | ### 2. Test MediaTailor Playback
38 |
39 | 1. To verify that ads are making it into your video, you may use a standalone video player to view the HLS playback endpoint such as QuickTime, VLC or any workstation-based player that supports HLS. Alternatively, you may use one of the following web-based players to stream your video:
40 |
41 | * http://videojs.github.io/videojs-contrib-hls/
42 | * https://developer.jwplayer.com/tools/stream-tester/
43 |
44 | 1. Your full playback URL will be the **HLS playback prefix** (eg. _https://f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/_)
45 | concatenated with the **manifest filename of the asset in MediaStore** (eg. _master.m3u8_)
46 |
47 | Provide the full playback URL to the player of your choice (eg. _https://f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/master.m3u8_)
48 |
49 | 1. With the VMAP response used here, you should see a 10-second preroll ad right at the beginning. Then, 15 seconds into the video, you should see a 10-second midroll ad. And finally, a 10-second postroll ad at the end. Note that it can take a few minutes for the ads to get transcoded and then properly stitched into the HLS manifest generated by AWS Elemental MediaTailor. This means that you may not see all the ads right away when you first play the asset back.
50 |
51 | ### 3. Integrate with Amazon CloudFront
52 |
53 | **Step-by-step instructions**
54 |
55 | #### 3a. Add Origins to Your Distribution
56 |
57 | 1. From the AWS Management Console, choose **Services** then select **CloudFront**.
58 |
59 | 1. Click on **Create Distribution**.
60 |
61 | 1. Select the **Web** delivery method for your content and hit the **Get Started** button.
62 |
63 | 1. Under **Origin Settings**, enter the domain name of your origin for **Origin Domain Name**. In our case, this is the domain name of our S3 bucket (eg. _vast-demo-bucket.s3.amazonaws.com_).
64 |
65 | 1. Under **Origin Protocol Policy**, select **HTTPS only**.
66 |
67 | 1. Take the default for all the other settings. But scroll down until you see the **Comment** textbox and enter `CloudFront for MediaTailor`.
68 |
69 | 1. Click the **Create Distribution** button. This will take you back to the main Distribution page of cloudfront. Your distribution will be in an **In Progress** state. The quickest way to tell which of the Distributions is yours is by the comment you entered.
70 |
71 | 1. Go back to the **Origins and Origin Group** tab, and click on the **Create Origin** button.
72 |
73 | 1. Enter MediaTailor's hostname for the **Origin Domain Name**. This will come from the **HLS playback prefix** of MediaTailor (e.g. _f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com_)
74 |
75 | 1. Update the **Origin Protocol Policy** to **HTTPS Only**. Click **Create**.
76 |
77 | 1. Go back to the **Origins** tab, and click on the **Create Origin** button.
78 |
79 | 1. Enter MediaTailor's ad server hostname for the **Origin Domain Name**. If MediaTailor is being set up in us-west-2 then the Origin is:
80 | `ads.mediatailor.us-west-2.amazonaws.com`
81 |
82 | 1. Update the **Origin Protocol Policy** to **HTTPS Only**. Click on **Create**.
83 |
84 | 
85 |
86 |
87 | #### 3b. Add Cache Behaviors to Your Distribution
88 |
89 | 1. Click on the **Behaviors** tab and click on the **Create Behavior** button.
90 |
91 | 1. Enter `/caminandes/*` for the **Path Pattern**.
92 |
93 | 1. Under **Origin**, select the S3 origin.
94 |
95 | 1. Click the **Create** button to add another cache behavior.
96 |
97 | 1. Click on **Create Behavior** button.
98 |
99 | 1. Enter `/v1/*` for the **Path Pattern**.
100 |
101 | 1. Under **Origin**, select the MediaTailor origin.
102 |
103 | 1. For **Query String Forwarding and Caching**, select **Forward all, cache based on all**.
104 |
105 | 1. Click the **Create** button to add another cache behavior.
106 |
107 | 1. Select the **Default** behavior, and click on the **Edit** button.
108 |
109 | 1. Make sure the **Origin** is pointed to the Ad Server origin (e.g. _ads.mediatailor.us-west-2.amazonaws.com_)
110 |
111 | 1. For **Query String Forwarding and Caching**, select **Forward all, cache based on all**.
112 |
113 | 1. Click on **Yes, Edit** button.
114 |
115 | 1. Double-check the precedence of the caching behavior as this matters. Your primary precedence should be the MediaTailor origin (precedence 0), followed by the S3 origin (precedence 1), and lastly by the ads origin (which is at Precedence 2 and is the Default). If this is not the precedence reflected, select one of the Behaviors and **Change Precedence** by clicking on either the **Move Up** or **Move Down** button, to make the adjustment.
116 |
117 | 
118 |
119 |
120 | ## Update MediaTailor Configuration with CloudFront Details
121 |
122 | 1. From the AWS Management Console, choose **Services** then select **AWS Elemental MediaTailor**.
123 |
124 | 1. Click on the Configuration (`MyTestCampaign`) you created in section 1 and hit the **Edit** button.
125 |
126 | 1. For the **CDN content segment prefix**, construct your URL by putting together the protocol, the CloudFront **Domain Name**, and the path of the MediaStore origin. For example: _https://**da4bw8c4fh0km.cloudfront.net**/caminandes_
127 |
128 | 1. For the **CDN ad segment prefix**, construct your URL by putting together the protocol, and the CloudFront **Domain Name**. For example: _https://da4bw8c4fh0km.cloudfront.net_
129 |
130 | 
131 |
132 |
133 | ## Test MediaTailor Playback with CloudFront
134 |
135 | 1. Once your CloudFront distribution is in the **Deployed** status, and **Enabled** state, try playing back your stream using the same player you selected in section 2 of this lab. Take your playback URL from Section 2, Step 2 and replace the MediaTailor hostname with the CloudFront **Domain Name** that was assigned to your distribution.
136 |
137 | For example, if your MediaTailor playback URL is:
138 |
139 | _https://**f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com**/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/master.m3u8_
140 |
141 | then your CloudFront playback URL is:
142 | _https://**da4bw8c4fh0km.cloudfront.net**/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/master.m3u8_)
143 |
144 |
145 |
146 | ## Completion
147 | Congratulations! You've successfully integrated your HLS video asset with AWS Elemental MediaTailor.
148 |
149 | Return to the [main](../README.md) page.
150 |
151 | ## Cloud Resource Clean Up
152 |
153 | ### AWS Elemental MediaTailor
154 | Select the configuration you created and hit the **Delete** button to clean up your resources.
155 |
156 | ### Amazon CloudFront
157 | Select the distribution that you created and hit the **Disable** button. Once the distribution's **State** reflects Disabled, select the same distribution and hit the **Delete** button.
158 |
--------------------------------------------------------------------------------
/11-VODMediaTailor/emt-vod-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/11-VODMediaTailor/emt-vod-workflow.png
--------------------------------------------------------------------------------
/11-VODMediaTailor/emt_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/11-VODMediaTailor/emt_config.png
--------------------------------------------------------------------------------
/12-AdMarkerInsertion/README.md:
--------------------------------------------------------------------------------
1 | # Monetizing VOD Assets with MediaTailor Through MediaConvert Ad Marker Insertion
2 |
3 | MediaConvert now has the capability to let you specify ad insertion points in your outputs. This is incredibly useful if your input video doesn't contain ad markers (SCTE-35 markers) but you still want to monetize your VOD assets or need to enforce blacking out certain content.
4 |
5 | In this tutorial, we show you how to use the ad marker insertion feature in MediaConvert through Event Signaling and Management(ESAM) XML documents. It is important to note that the ad markers you insert will not lengthen the input asset to accomodate for the ad you may want to insert in the future. It will simply mark the position(s) in the stream where these ads should go, or where blackouts should happen. This is something to consider if you want to insert a pre-, mid-, and/or post-roll to your asset without overwriting actual content.
6 |
7 | For the video ad insertion portion of this tutorial, we will take advantage of the manifest conditioning feature in MediaConvert that can be enabled in addition to signal conditioning. HLS manifest from MediaConvert output will have CUE-IN/OUT tags corresponding to the ad markers in the stream. MediaTailor, in turn, will use the HLS manifest decorations to figure out when and where to insert ads coming from an Ad Decision Server(ADS) using VAST.
8 |
9 | ## Assumptions and Prerequisites
10 | This tutorial has a few assumptions and prerequisites.
11 |
12 | You should be familiar with:
13 | * Event signaling and messaging(ESAM). Refer to [the specification provided by CableLabs](https://specification-search.cablelabs.com/real-time-event-signaling-and-management-api-2?v=03).
14 |
15 | You have:
16 | * A bucket in S3 that you have write access to. Referred to in this tutorial as **MyBucket**.
17 | * An IAM role for your MediaConvert job; If not, follow [this tutorial](../1-IAMandS3/README-user.md)
18 |
19 | ## What's Provided
20 | The tutorial uses and references the following resources:
21 |
22 | * [An MP4 asset](https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/llama_with_slates.mp4) stored in S3 for input. This asset has a slate for 15 seconds at the beginning (pre-roll), followed by 60 seconds of content, then followed by another slate for 15 seconds (mid-roll), then a slate for 15 seconds at the end (post-roll). It does not have ad markers.
23 | * [Sample VAST response](http://d2qohgpffhaffh.cloudfront.net/MediaTailor/VASTDemo.xml) hosted on S3 as our Ad Decision Server(ADS)
24 | * [Sample signal processing notification XML](emc-sample-signal-notification.xml)
25 | * [Sample manifest confirm condition notification XML](emc-sample-manifest-conditioning.xml)
26 |
27 | ## Implementation Instructions
28 |
29 | ### Create an AWS Elemental MediaConvert Job
30 | #### Inputs section
31 |
32 | 1. Open the [MediaConvert console](https://us-west-2.console.aws.amazon.com/mediaconvert/home?region=us-west-2#/welcome) for the region you are completing the lab in (US-West-Oregon).
33 | 1. Select **Jobs** from the side bar menu.
34 | 1. Select **Create job** to open the Create job page.
35 | 1. Select **Input 1** on the Job panel to open the form for Input 1.
36 | 1. Enter the file name `s3://rodeolabz-us-west-2/vodconsole/llama_with_slates.mp4` in the box under Input
37 |
38 | #### Add an Apple HLS Output Group
39 | For simplicity, we will only have one output to this HLS output group.
40 |
41 | 1. Under **Output Groups** on the Job panel, select **Add**.
42 | 1. Check the **Apple HLS** box then click on **Select**.
43 | 1. Make sure **Apple HLS** is selected in the Job panel in the Output groups section so that Apple HLS form is loaded.
44 | 1. Fill in `HLS` in the **Custom group name** box.
45 | 1. In the **Destination** textbox, point to your S3 bucket and give it a path like:
46 | `s3://MyBucket/HLS/llama`
47 | 1. Under **Segment Control**, set the **Minimum segment length** to `3`. This is to help ad marker and content segment alignment.
48 |
49 | 
50 | 1. Select Output 1 from the Output Groups section Jobs panel to go to the Output settings form.
51 | 1. Enter `_720` in the **Name modifier** box
52 | 1. Enter `$dt$` in the **Segment modifier** box.
53 | 1. Scroll down to the **Stream settings** panel and enter `1280` and `720` in the **Resolution (w x h)** box
54 | 1. Scroll down to the **Bitrate (bits/s)** box and enter `3000000`.
55 |
56 | #### Job settings section
57 |
58 | 1. Select **Settings** from the **Job settings** section of the Job panel to open the **Job settings** form.
59 | 1. Select the MediaConvert role you've previously created from the **IAM role** dropdown.
60 |
61 | 
62 | 1. Scroll down to the **Ad signaling** section. Enable **Event signaling and messaging (ESAM)**.
63 | 1. Under **Signal processing notification XML**, copy/paste the content of [this XML](emc-sample-signal-notification.xml) into the textbox.
64 | 1. Under **Manifest confirm condition notification XML**, copy/paste the content of [this XML](emc-sample-manifest-conditioning.xml) into the textbox.
65 |
66 | 
67 |
68 | #### Create the job
69 | 1. Scroll to the bottom of the page and select **Create**
70 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job details** page using the **refresh** button.
71 |
72 |
73 | #### View outputs in S3
74 |
75 | 1. In the AWS Management Console choose **Services** then select **S3** under Storage.
76 | 1. Select the bucket you chose for your output destination. You should find a folder called `HLS`. Your HLS outputs should be in there.
77 | 1. If your bucket is not publicly accessible, select all the outputs, and under **Actions**, select **Make public**.
78 |
79 |
80 | ### Insert ads
81 | At this point, we have put markers in the HLS transport stream, as well as CUE-IN/OUT tags in the manifest, corresponding to the slates in our asset. Let's now replace those slates with actual ads with the help of MediaTailor.
82 |
83 | #### Create an AWS Elemental MediaTailor Configuration
84 |
85 | 1. From the AWS Management Console, choose **Services** then select **MediaTailor**.
86 | 1. Click on **Create configuration**.
87 | 1. Enter `MyTestCampaign` for the **Configuration Name**.
88 | 1. For the **Video content source**, enter the S3 URL link to your HLS asset but **_without the manifest filename_**. Should look something like below, making sure to replace `MyBucket` with the name of your bucket:
89 | `https://s3-us-west-2.amazonaws.com/MyBucket/HLS/`
90 |
91 | 1. Enter `http://d2qohgpffhaffh.cloudfront.net/MediaTailor/VASTDemo.xml` for the **Ad decision server**.
92 |
93 | 
94 |
95 | 1. Click **Create Configuration**. Click on the **Configurations** link to see the configuration you just created. Click on **MyTestCampaign** to see the **Playback endpoints** populated with playback URLs. Note down the **HLS playback prefix** as you'll need it in the next section.
96 |
97 |
98 | #### 2. Test MediaTailor Playback
99 | 1. To verify that ads are making it into your video, you may use a standalone video player to view the HLS playback endpoint such as QuickTime, VLC or any workstation-based player that supports HLS. Alternatively, you may use one of the following web-based players to stream your video:
100 |
101 | * http://videojs.github.io/videojs-contrib-hls/
102 | * https://developer.jwplayer.com/tools/stream-tester/
103 |
104 | 1. Your full playback URL will be the **HLS playback prefix** (eg. _https://f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/_)
105 | concatenated with the **manifest filename of the asset in S3** (eg. _llama.m3u8_)
106 |
107 | Provide the full playback URL to the player of your choice (eg. _https://f445cfa805184f3e8d86dc2ac1137efa.mediatailor.us-west-2.amazonaws.com/v1/master/cf6421621b389b384c1fd22e51603ee95db76ae0/MyTestCampaign/llama.m3u8_)
108 | 1. You should see an ad in place of the slates for the pre-, mid-, and post-roll.
109 |
110 |
111 | ## Completion
112 | Congratulations! You've successfully added ad markers to your asset, and monetized your content.
113 |
114 | Return to the [main](../README.md) page.
115 |
116 | ## Resource Clean Up
117 |
118 | ### AWS Elemental MediaTailor
119 | Select the configuration you created and hit the **Delete** button to clean up your resources.
120 |
121 | ### Amazon S3
122 | Once you're done viewing and testing your HLS output, navigate to your S3 bucket and delete the HLS segments and manifest.
123 |
124 | ## References
125 | * [Event signaling and Messaging(ESAM) specification from CableLabs](https://specification-search.cablelabs.com/real-time-event-signaling-and-management-api-2?v=03)
126 | * [Documentation on including SCTE-35 markers in MediaConvert outputs](https://docs.aws.amazon.com/mediaconvert/latest/ug/including-scte-35-markers.html)
127 | * [VOD content ad behavior in MediaTailor](https://docs.aws.amazon.com/mediatailor/latest/ug/ad-behavior-vod.html)
128 |
129 |
130 |
--------------------------------------------------------------------------------
/12-AdMarkerInsertion/emc-sample-manifest-conditioning.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/12-AdMarkerInsertion/emc-sample-signal-notification.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | PT15.000S
47 |
48 |
49 | PT15.000S
50 |
51 |
52 | PT15.000
53 |
54 |
--------------------------------------------------------------------------------
/13-VODMediaPackage/README-tutorial.md:
--------------------------------------------------------------------------------
1 | # Delivering VOD Content from AWS Elemental MediaPackage
2 | AWS Elemental MediaPackage can now package, encrypt, and originate on-demand video. This means you can package file-based video to multiple distribution formats without having to transcode video again. A video asset can be ingested into a MediaPackage VOD pacakaging group, and made available for playback on a variety of devices. The MediaPackage VOD workflow lends itself well as a MediaConvert job post-process for jobs that output HLS.
3 |
4 | In this tutorial, we walk you through ingesting an HLS asset you have transcoded through MediaConvert for further packaging through the MediaPackage console. If you'd like to see how this could be done using a CloudWatch event rule and a Lambda, [follow this other tutorial](README.md).
5 |
6 | ## Assumptions and Prerequisites
7 | * Completion of [2-MediaConvertJobs](../2-MediaConvertJobs/README.md)
8 |
9 | ## Implementation Instructions
10 |
11 | ### Give MediaPackage access to S3
12 | You want to give MediaPackage access to the S3 bucket MediaConvert has been using to write outputs.
13 |
14 | 1. Follow the instructions here carefully: https://docs.aws.amazon.com/mediapackage/latest/ug/setting-up-create-trust-rel.html. At the last section, make sure that your trusted service is **mediapackage.amazonaws.com** as shown in the JSON policy document.
15 | 1. Note the MediaPackage role name you created.
16 |
17 | ### Create a Packaging Group
18 | 1. From the MediaPackage console, expand the **Video on Demand** menu on the left hand side navigation.
19 | 1. Click on **Packaging Groups**.
20 | 1. Click on **Create** button.
21 | 1. Enter `MyPackagingGroup` for the Packaging Group Id and click the **Create** button.
22 | 1. Under **General Settings** in the Manage Packaging Configuration window, enter `DASH_Config` for the Id.
23 | 1. Under **Package Type**, select `DASH-ISO`.
24 | 1. On the left hand side navigation, click on **Add**, and select **New Config** to add another configuration.
25 | 1. Under **General Settings**, enter `Smooth_Config` for the Id.
26 | 1. Under **Package Type**, select `Microsoft Smooth`.
27 | 1. On the left hand side navigation, click on **Add**, and select **New Config** to add another configuration.
28 | 1. Under **General Settings**, enter `CMAF_Config` for the Id.
29 | 1. Under **Package Type**, select `Common Media Application Format(CMAF)`.
30 | 1. Click on **Save** button.
31 |
32 | 
33 |
34 | ### Ingest an Asset
35 | 1. From the MediaPackage console, expand the **Video on Demand** menu on the left hand side navigation.
36 | 1. Click on **Assets**.
37 | 1. Click on **Ingest Asset** button.
38 | 1. Under **S3 bucket name**, select the bucket where MediaConvert wrote your HLS output.
39 | 1. Under **IAM Role**, select the MediaPackage role you created at the beginning of this tutorial.
40 | 1. Under **Asset Details**, select `assets/VANLIFE/HLS/VANLIFE.m3u8` from the dropdown for the **Filename**.
41 | 1. Under **Packaging Settings**, select the packaging group you created earlier `MyPackagingGroup` from the dropdown.
42 | 1. Click on **Ingest Assets** button.
43 |
44 | 
45 |
46 | ### Playback the MediaPackage Endpoints
47 | We will use a demo player to play back the various endpoints created by MediaPackage.
48 | 1. Back on the Assets page, select the Asset you just created.
49 | 1. You should find the endpoints created by MediaPackage under **Playback details**. There should be one endpoint for each of the configuration you set up in your packaging group.
50 | 
51 | 1. Copy the URL of the first ingest endpoint.
52 | 1. Navigate to `https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/reinvent2019/vodplayer.html`
53 | 1. Paste the URL you copied on to the Custom URL textbox and hit the Preview button.
54 | 1. Preview the rest of the endpoints.
55 |
56 | 
--------------------------------------------------------------------------------
/13-VODMediaPackage/README.md:
--------------------------------------------------------------------------------
1 | # Delivering VOD Content from AWS Elemental MediaPackage
2 | AWS Elemental MediaPackage can now package, encrypt, and originate on-demand video. This means you can package file-based video to multiple distribution formats without having to transcode video again. A video asset can be ingested into a MediaPackage VOD pacakaging group, and made available for playback on a variety of devices. The MediaPackage VOD workflow lends itself well as a MediaConvert job post-process for jobs that output HLS.
3 |
4 | In this tutorial, we walk you through ingesting an HLS asset you have transcoded through MediaConvert for further packaging in a more automated fashion. You will do this by first subscribing to the MediaConvert job completion event notification through CloudWatch. You then use a Lambda to process that CloudWatch event notification and create a MediaPackage VOD asset based on the HLS output of the MediaConvert job.
5 |
6 | If you'd like to see how you would ingest an HLS asset through the MediaPackage console without the use of Lambdas, then follow [this other tutorial](README-tutorial.md).
7 |
8 | ## Assumptions and Prerequisites
9 | * Completion of [MediaConvert Jobs](../2-MediaConvertJobs/README.md) or [MediaConvert Jobs via WatchFolder](../7-MediaConvertJobLambda/README.md).
10 |
11 | ## Implementation Instructions
12 |
13 | ### Give MediaPackage access to S3
14 | You want to give MediaPackage access to the S3 bucket MediaConvert has been using to write outputs.
15 |
16 | 1. Follow the instructions here https://docs.aws.amazon.com/mediapackage/latest/ug/setting-up-create-trust-rel.html.
17 | 1. Note the **MediaPackage role ARN** you created. This will be the **Source Role ARN** you will provide in your Lambda function later.
18 |
19 | ### Create a Lambda Execution Role
20 | The Lambda you're going to be creating will need specific permissions in order to be able to create the MediaPackage Packaging Group and VOD Asset.
21 |
22 | 1. Navigate to the IAM console.
23 | 1. Click on **Policies** on the left hand side menu.
24 | 1. Click on **Create Policy**.
25 | 1. Click on the **JSON** tab.
26 | 1. Replace the policy with the following:
27 |
28 | ```JSON
29 | {
30 | "Version": "2012-10-17",
31 | "Statement": [
32 | {
33 | "Sid": "EMPVOD",
34 | "Effect": "Allow",
35 | "Action": [
36 | "mediapackage-vod:*"
37 | ],
38 | "Resource": "*"
39 | },
40 | {
41 | "Sid": "PassRole",
42 | "Effect": "Allow",
43 | "Action": "iam:PassRole",
44 | "Resource": "*"
45 | }
46 | ]
47 | }
48 | ```
49 | 1. Click on **Review Policy**.
50 | 1. Give the policy a name like `MediaPackageVODPolicy`.
51 | 1. Click on **Create Policy**.
52 | 1. Back on the IAM console, click on **Roles** on the left hand side menu.
53 | 1. Click on **Create Role**.
54 | 1. Under **Select service that will use this role**, select **Lambda**.
55 | 1. Click on **Next:Permissions**.
56 | 1. On the Search textbox, enter the name of the Policy you created earlier (eg. `MediaPackageVODPolicy`).
57 | 1. Click on the checkbox next to the Policy to select it.
58 | 1. Click on **Next:Tags**.
59 | 1. Click on **Next:Review**.
60 | 1. Give the Role a name like `MediaPackageVODRoleForLambda`.
61 | 1. Click on **Create Role**.
62 |
63 |
64 | ### Create the MediaPackage VOD Asset Lambda
65 | Now that the IAM Role for the Lambda has been created, it's time to create the function. This function will create a Packaging Group, with 3 configurations: one for Microsoft Smooth, one for CMAF, and one for DASH.
66 |
67 | 1. Navigate to the AWS Lambda console.
68 | 1. Click on **Create Function**.
69 | 1. Provide a **Function Name** like `CreateVodAsset`.
70 | 1. From the **Runtime** dropdown, select `Python 3.7`.
71 | 1. Expand the **Choose or create an execution role** section.
72 | 1. Select **Use an existing role.**.
73 | 1. From the **Existing role** dropdown, select the Role you created earlier (eg. `MediaPackageVODRoleForLambda`).
74 |
75 | 
76 | 1. Click on **Create Function**.
77 | 1. Scroll down to the **Function Code** section.
78 | 1. Copy the code in the [ingest_asset Lambda provided here](ingest_asset.py) and paste the content on to the lambda_function window, replacing the sample code that's in there.
79 | 1. Scroll down to the **Environment variables** section.
80 | 1. Add two environment variables:
81 | * PACKAGING_GROUP_ID - choose a packaging name like `MyPackagingGroup`
82 | * SOURCE_ROLE_ARN - this is the MediaPackage Role ARN you created in [this section](#mediapackage-access-s3).
83 |
84 | 
85 | 1. Scroll down to **Basic settings** section.
86 | 1. Set **Timeout** to a `1 min`.
87 | 1. Click on the **Save** button at the top to save the Lambda.
88 |
89 | ### Create an Event Rule in CloudWatch
90 | You will now create a notification rule that matches MediaConvert COMPLETE event. The target of the rule is the Lambda you just created.
91 |
92 | 1. Open the CloudWatch console page and select the **Rules** link on the side-bar menu under Events.
93 | 1. Click on the **Create rule** button.
94 | 1. Under **Service Name** in the **Event Source** section, search for `MediaConvert`.
95 | 1. For **Event Type**, select `MediaConvert Job State Change`.
96 | 1. Select **Specific state(s)**,
97 | 1. From the dropdown, select `COMPLETE`.
98 | 1. In the **Targets** section, click on **Add Target**. This will default to **Lambda function**.
99 | 1. In the function dropdown, select the Lambda you just created (eg. `CreateVodAsset`).
100 |
101 | 
102 | 1. Click on **Configure details**.
103 | 1. Give the Rule definition a name like `MediaPackageCreateAsset`.
104 | 1. Click on **Create rule**.
105 |
106 |
107 | ### Run a MediaConvert Job with HLS Output
108 | To trigger the event rule you just created in CloudWatch, and therefore the Lambda that will create the MediaPackage VOD asset, you need to create and run a job with an HLS output. You can do this by either:
109 |
110 | * Duplicating and running one of the jobs you created in [MediaConvert Jobs]().
111 |
112 | or
113 |
114 | * Uploading a test file to your watchfolder as you did in [MediaConvert Job Lambda]() to trigger a job creation.
115 |
116 |
117 | ### Verify the MediaPackage VOD Asset Creation
118 | Once the MediaConvert job has completed, it is time to check and see if the corresponding MediaPackage VOD asset got created by your Lambda.
119 |
120 | 1. From the MediaPackage console, expand the **Video on Demand** menu on the left hand side navigation.
121 | 1. Click on **Assets**.
122 | 1. You should see an asset with a name like the filename of your HLS output, appended with a timestamp.
123 |
124 | 
125 |
126 |
127 | ### Playback the MediaPackage Endpoints
128 | To make sure that the assets are actually working, play back the various endpoints that got generated using a demo player. There should be 3 endpoints, corresponding to the 3 configurations in the Packaging Group that the Lambda created for you.
129 |
130 | 1. Back on the Assets page on the MediaPackage console, select the Asset that got created.
131 | 1. You should find the endpoints created by MediaPackage under **Playback details**.
132 |
133 | 
134 |
135 | 1. Copy the first playback URL.
136 | 1. Navigate to `https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/reinvent2019/vodplayer.html`
137 | 1. Paste the URL you copied on to the Custom URL textbox and hit the Preview button.
138 | 1. Preview the rest of the endpoints.
139 |
140 | 
--------------------------------------------------------------------------------
/13-VODMediaPackage/ingest_asset.py:
--------------------------------------------------------------------------------
1 | import json
2 | import boto3
3 | import time
4 | import os
5 |
6 | def lambda_handler(event, context):
7 | empvod = boto3.client('mediapackage-vod')
8 |
9 | packaging_group_id = os.environ['PACKAGING_GROUP_ID']
10 | hls_playlist_paths = []
11 | for output_detail in event['detail']['outputGroupDetails']:
12 | if output_detail['type'] == 'HLS_GROUP':
13 | hls_playlist_paths = output_detail['playlistFilePaths']
14 | if len(hls_playlist_paths) > 0:
15 | for hls_path in hls_playlist_paths:
16 | try:
17 | #check to see if packaging group exists
18 | response = empvod.describe_packaging_group(
19 | Id=packaging_group_id
20 | )
21 | except empvod.exceptions.NotFoundException as e:
22 | print(e)
23 | # if packaging group not found, create one plus 3 configurations
24 | response = empvod.create_packaging_group(
25 | Id=packaging_group_id
26 | )
27 | # create the packaging configurations, taking mostly defaults
28 | # CMAF
29 | response = empvod.create_packaging_configuration(
30 | CmafPackage = {
31 | 'HlsManifests': [
32 | {
33 | },
34 | ],
35 | 'SegmentDurationSeconds': 2
36 | },
37 | Id = packaging_group_id + "_CMAF_config",
38 | PackagingGroupId = packaging_group_id
39 | )
40 | # Smooth
41 | response = empvod.create_packaging_configuration(
42 | MssPackage={
43 | 'MssManifests': [
44 | {
45 | },
46 | ],
47 | 'SegmentDurationSeconds': 2
48 | },
49 | Id = packaging_group_id + "_MSS_config",
50 | PackagingGroupId = packaging_group_id
51 | )
52 | # DASH
53 | response = empvod.create_packaging_configuration(
54 | DashPackage={
55 | 'DashManifests': [
56 | {
57 | },
58 | ],
59 | 'SegmentDurationSeconds': 2
60 | },
61 | Id = packaging_group_id + "_DASH_config",
62 | PackagingGroupId = packaging_group_id
63 | )
64 |
65 | # create asset after packaging group has been verified or created
66 | source_arn = "arn:aws:s3:::" + hls_path.split('s3://')[1]
67 | #asset_id is manifest file name + current time
68 | asset_id = hls_path.split('/')[-1].split('.')[0] + str(int(time.time()))
69 | response = empvod.create_asset(
70 | Id=asset_id,
71 | PackagingGroupId=packaging_group_id,
72 | SourceArn=source_arn,
73 | SourceRoleArn=os.environ['SOURCE_ROLE_ARN']
74 | )
75 |
76 | return {
77 | 'statusCode': 200,
78 | 'body': json.dumps(response)
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/2-MediaConvertJobs/ExportImportJob.md:
--------------------------------------------------------------------------------
1 | # Exporting and Importing AWS Elemental MediaConvert Jobs
2 |
3 | Completed MediaConvert jobs remain on the Jobs page for three months. If you want to be able to run a new job based on a completed job more than three months after you run it, export the job after it has completed and save it.
4 |
5 | Depending on how many jobs you've already ran, exporting and then importing a job can be simpler than finding a particular job in your list and duplicating it.
6 |
7 | There are a few other reasons you might want to export a completed job. You want to:
8 | * Run the same job in a different region it was originally ran in
9 | * Run the same job in a different account
10 | * Use the exported JSON as a starting point for automating job creation
11 |
12 | ## Prerequisites
13 |
14 | A completed MediaConvert job available through the console.
15 | If you've not run a MediaConvert job before, follow [this tutorial](README.md).
16 |
17 | ## Export a Completed Job
18 |
19 | 1. From the MediaConvert Jobs page, click on one of the jobs that you ran and has completed.
20 | 1. On the Job Summary page, click on the **Export JSON** button.
21 | 1. Save the JSON file on your local machine, noting the location where you saved.
22 | 1. Open this saved file using your favorite editor. Let's make a small edit to the job before importing it back into MediaConvert.
23 | 1. Under the **Settings** key, you'll find the output groups of the job.
24 | 1. Rearrange the output groups so that a different output group is at the top.
25 |
26 | 
27 |
28 | 1. Save the file.
29 |
30 |
31 | ## Import the Saved Job
32 |
33 | 1. From the MediaConvert Jobs page, click on **Import Job**.
34 |
35 | 1. Upload the exported job file (JSON) that you just edited and saved.
36 |
37 | 1. Confirm that the order of the Output Groups is now in the order you set it to.
38 |
39 | 1. Scroll to the bottom and click on **Create**.
40 |
41 | 1. Job should complete without errors.
--------------------------------------------------------------------------------
/2-MediaConvertJobs/MediaConvertJobs.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates a bucket to store inputs and outputs for MediaConvert and a role to
6 | pass to MediaConvert to access the bucket and other account resources
7 | MediaConvert needs to process jobs from the console and API. For the lab,
8 | the bucket is website enabled so that output videos can play out as web resources.
9 |
10 | Parameters:
11 | MediaBucket:
12 | Type: String
13 | Description:
14 | The name for the bucket you want to use for MediaConvert
15 | inputs and ouputs, e.g. 'vod-yourname.'
16 |
17 | Resources:
18 |
19 | MediaBucket:
20 | Properties:
21 | BucketName:
22 | !Join
23 | - '-'
24 | - - !Ref MediaBucketName
25 | - !Ref 'AWS::AccountId'
26 | WebsiteConfiguration:
27 | IndexDocument: index.html
28 | CorsConfiguration:
29 | CorsRules:
30 | - AllowedHeaders: ['*']
31 | AllowedMethods: [GET]
32 | AllowedOrigins: ['*']
33 | ExposedHeaders: [Date]
34 | Id: myCORSRuleId1
35 | MaxAge: '3600'
36 | LifecycleConfiguration:
37 | Rules:
38 | - Id: ExpireRule
39 | Status: Enabled
40 | ExpirationInDays: '2'
41 | DeletionPolicy: Retain
42 | Type: "AWS::S3::Bucket"
43 |
44 | MediaBucketPolicy:
45 | Properties:
46 | Bucket: !Ref MediaBucket
47 | PolicyDocument:
48 | Version: 2012-10-17
49 | Statement:
50 | -
51 | Effect: Allow
52 | Principal: "*"
53 | Action: s3:GetObject
54 | Resource: !Sub "arn:aws:s3:::${MediaBucket}/*"
55 | Type: "AWS::S3::BucketPolicy"
56 |
57 | Outputs:
58 | MediaBucket:
59 | Value: !Ref MediaBucket
60 | WebsiteURL:
61 | Value: !Join ['', ['https://', !GetAtt [MediaBucket, DomainName]]]
62 | Description: Name of S3 bucket to hold website content
63 |
--------------------------------------------------------------------------------
/3-Inputs/README.md:
--------------------------------------------------------------------------------
1 | # Module 3: Modifying AWS Elemental MediaConvert Inputs
2 |
3 | In this section we will introduce an example of how video inputs can be modified as part of encoding and packaging with MediaConvert.
4 |
5 | Video content often needs to be combined or _stitched_ with other videos. For example, if you have a YouTube channel, you may want to always play a channel clip at the begining of a piece of content and invite viewers to subscribe to the channel or promote other content at the end of each piece of content. These are referred to as _bumper_ videos.
6 |
7 | Sometimes you may also want to clip the input videos before you stitch them together. For example, you want to make a _sizzle reel_ that combines multiple clips of larger videos into a single video you can use for promotion.
8 |
9 | In this module you'll modify the AWS Elemental MediaConvert job you created in the last module to combine several video inputs into a single video.
10 |
11 | ## Prerequisites
12 |
13 | You need to have access to MediaConvert and S3 to complete this module.
14 |
15 | You need the following resources created in module 1:
16 | * **MediaConvertRole** - the role created to give permission for MediaConvert to access resources in your account.
17 | * **MediaBucket** - the bucket created to store outputs from MediaConvert.
18 | * **MediaConvert job from module 2** - We will start with this job and modify it in this module.
19 |
20 | If you used CloudFormation to configure resources, you will find the values names of MediaConvertRole and MediaBucket in the Outputs of the Stack.
21 |
22 | ## 1. Modify a MediaConvert job to add input clipping and stitching
23 |
24 | Create duplicate of the job you created in module 2. Modify the job to clip the first minute of the vanlife video and then stitch on a trailing video at the end. The new job will have the following structure:
25 |
26 | 
27 |
28 | #### Detailed instructions
29 |
30 | #### Duplicate the job from the previous module
31 |
32 | 1. Open the MediaConvert console for the region you are completing the lab in (US-West-Oregon). https://us-west-2.console.aws.amazon.com/mediaconvert/home?region=us-west-2#/welcome
33 | 1. Select **Jobs** from the side bar menu.
34 | 1. Find the job you created in the last module and click on the Job Id link to open the **Job details** page.
35 | 1. Select **Duplicate**
36 |
37 | #### Clip the first 30 seconds of the Input 1 video
38 |
39 | 1. Select **Input 1** from the menu sidebar.
40 | 1. Scroll down to the **Video selector** panel and select **Start at 0** from the **Timecode source** dropdown.
41 |
42 | 
43 | 1. Scroll down to the **Input clips** panel and select **Add input clip**.
44 | 1. Enter `00:00:00:00` in the **Start timecode** box for **Input clipping 1**
45 | 1. Enter `00:00:30:00` in the **End timecode** box for **Input clipping 1**
46 |
47 | 
48 |
49 | #### Stitch the TRAILER video to the end of the VANLIFE video.
50 |
51 | 1. In the Input section of the MediaConvert side bar menu, select **Add**.
52 | 1. Make sure **Input 2** is selected from the Input section of the MediaConvert side bar menu.
53 | 1. Enter `s3://rodeolabz-us-west-2/vodconsole/TRAILER.mp4` in the box on the **Input 2** panel.
54 |
55 | 
56 |
57 | #### Create the job
58 |
59 | 1. Scroll to the bottom of the page and select **Create**
60 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job detail** page.
61 |
62 | ### 2. Play the videos
63 |
64 |
65 | To play the videos, you will use the S3 HTTPS resource **Link** on the videos S3 object **Overview** page.
66 |
67 | 
68 |
69 |
70 | #### MP4s
71 |
72 | The MP4 output is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/MP4/VANLIFE.mp4
73 |
74 | You can play the MP4 using:
75 | * Chrome by clicking on the **Link** for the object.
76 | * **JW Player Stream Tester** by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
77 |
78 | #### HLS
79 |
80 | The HLS manifest file is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/HLS/VANLIFE.m3u8
81 |
82 | You can play the HLS using:
83 | * Safari browser by clicking on the **Link** for the object.
84 | * **JW Player Stream Tester** - by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
85 |
86 | #### Thumbnails
87 |
88 | You should be able to view thumbnail JPEG files using your browser.
89 |
90 | ### Video preview
91 |
92 | The HLS, MP4 and Thumbnail outputs should be modified to play the first 30 seconds of the VANLIFE video, followed by a 21 second clip of aerial footage of some sea stacks.
93 |
94 | 
95 |
96 | ## Completion
97 |
98 | Congratulations! You have successfully created video conversion job with multiple inputs and outputs for AWS Elemental MediaConvert. Move forward to the next module to add watermarks to your job outputs.
99 |
100 | Next module: [**Modifying AWS Elemental MediaConvert Outputs**](../4-Outputs/README.md)
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/4-Outputs/README.md:
--------------------------------------------------------------------------------
1 | # Module 4: Modifying AWS Elemental MediaConvert Outputs
2 |
3 | In this section we will introduce two examples of how video outputs can be modified by MediaConvert as part of the encoding and packaging workflow.
4 |
5 | You will modify the HLS output by burning in timecodes and a label with the resolution of each quality level of the ABR stack. Timecode burn in is often used in the video editing process to make accurate notations referencing parts of a video.
6 |
7 | You will modify the MP4 output by inserting an image or watermark. Watermarks are often used when you want create a shareable version of your video that is difficult to be reused without your permission. You may want to burn in copyrights or add a watermark logo that covers the entire video. Your viewers can see a preview of the content, but the preview video is not very useful for other purposes.
8 |
9 | The new MediaConvert job will have the following structure:
10 |
11 | 
12 |
13 |
14 | ## Prerequisites
15 |
16 | You need to have access to MediaConvert and S3 to complete this module.
17 |
18 | You need the following resources created in module 1:
19 | * **MediaConvertRole** - the role created to give permission for MediaConvert to access resources in your account.
20 | * **MediaBucket** - the bucket created to store outputs from MediaConvert.
21 | * **MediaConvert job from module 2 or later** - We will start with this job and modify it in this module.
22 |
23 | If you used CloudFormation to configure resources, you will find the values names of MediaConvertRole and MediaBucket in the Outputs of the Stack.
24 |
25 | ## 1. Modify the HLS output to include a timecode
26 |
27 | You will burn in a timecode and a tag that indicates which video from the HLS output group is currently selected by the player.
28 |
29 | #### Duplicate the job from the previous module
30 |
31 | 1. Open the MediaConvert console for the region you are completing the lab in (US-West-Oregon). https://us-west-2.console.aws.amazon.com/mediaconvert/home?region=us-west-2#/welcome
32 | 1. Select **Jobs** from the side bar menu.
33 | 1. Find the job you create in the previous module and click on the Job Id link to open the **Job details** page.
34 | 1. Select **Duplicate**
35 |
36 | #### Add timecode burn in for the HLS outputs
37 |
38 | 1. Select **Output 1** from the **Apple HLS Group ** of the Output Groups section of the MediaConvert menu side bar to open the **Output settings** form for Output 1.
39 |
40 | 1. Scroll down to the **Encoding Settings** panel and select **Video 1** from the side bar.
41 | 1. Expand the **Preprocessors** carrot and change the **Timecode burn-in** switch to the on position.
42 | 1. Select **Small (16)** from the **Font size** drop down.
43 | 1. Enter `640x360_` plus your name or some other message, such as an ASCII art monkey `@('_')@`, in the **Prefix** box
44 |
45 | 
46 |
47 | 1. Repeat this step for **Output 2** and **Output 3** using **Prefix** `960x540_` for **Output 2** and **Prefix** `1280x720_` for **Output 3**.
48 |
49 | #### Create the job
50 |
51 | 1. Scroll to the bottom of the page and select **Create**
52 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job detail** page.
53 |
54 | ### 2. Play the HLS video
55 |
56 | The HLS manifest file is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/HLS/VANLIFE.m3u8
57 |
58 | You can play the HLS using:
59 | * Safari browser by clicking on the **link** for the object.
60 | * **JW Player Stream Tester** - by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
61 |
62 | #### Video preview
63 |
64 | The HLS output should be modified with timcodes. The MP4 and Thumbnails outputs should be unchanged. You may notice that the resolution chosen by the player changes as the video plays over time. This is normal, in fact, it is the _adaptive_ feature in ABR at work. The player adapts the bitrate of the video based on sampled bandwith over time.
65 |
66 | 
67 |
68 |
69 | ### 3. Add a watermark to the MP4 output
70 |
71 | 1. In the **Preprocessors** section of the Encoding settings panel for Video 1 change the **Image inserter** switch to the on position.
72 | 1. Select **Add image**
73 | 1. Enter `s3://rodeolabz-us-west-2/vodconsole/WATERMARK_wave.png` in the **Image location** box.
74 | 1. Enter `0` in the **Layer** box.
75 | 1. Enter `0` in the **Left** box.
76 | 1. Enter `518` in the **Top** box.
77 | 1. Enter `60` in the **Opacity** box.
78 | 1. Enter `837` in the **Width** box.
79 | 1. Enter `164` in the **Height** box.
80 | 1. Leave the rest of the settings empty.
81 |
82 | 
83 |
84 | #### Create the job
85 |
86 | 1. Scroll to the bottom of the page and select **Create**
87 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job detail** page.
88 |
89 | ### 4. Play the MP4
90 |
91 | To play the videos, you will use the S3 HTTPS resource **Link** on the videos S3 object **Overview** page.
92 |
93 | 
94 |
95 |
96 | #### MP4s
97 |
98 | The MP4 output is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/MP4/VANLIFE.mp4
99 |
100 | You can play the MP4 using:
101 | * Chrome by clicking on the **Link** for the object.
102 | * **JW Player Stream Tester** by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
103 |
104 | ### Video preview
105 |
106 | The MP4 output should be modified with a watermark. The HLS and Thumbnails outputs should be unchanged.
107 |
108 | 
109 |
110 | ## Completion
111 |
112 | Move forward to the next module to work captions.
113 |
114 | Next module: [**Working with Captions**](../5-Captions/README.md)
115 |
116 |
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/5-Captions/CAPTIONS_en.srt:
--------------------------------------------------------------------------------
1 |
2 | 1
3 | 00:00:00,250 --> 00:00:10,500
4 | This blade has a dark past.
5 |
6 | 2
7 | 00:00:11,800 --> 00:00:20,800
8 | It has shed much innocent blood.
9 |
10 | 3
11 | 00:00:21,800 --> 00:00:39,800
12 | You're a fool for traveling alone,
13 | so completely unprepared.
14 |
15 | 4
16 | 00:00:40,800 --> 00:00:49,800
17 | You're lucky your blood's still flowing.
18 |
19 | 5
20 | 00:00:50,800 --> 00:00:59,800
21 | Thank you.
22 |
23 | 6
24 | 00:01:00,800 --> 00:01:09,800
25 | So...
26 |
27 | 7
28 | 00:01:10,800 --> 00:01:19,800
29 | What brings you to
30 | the land of the gatekeepers?
31 |
32 | 8
33 | 00:01:20,800 --> 00:01:29,800
34 | I'm searching for someone.
35 |
36 | 9
37 | 00:01:30,800 --> 00:01:39,800
38 | Someone very dear?
39 | A kindred spirit?
40 |
41 | 10
42 | 00:01:40,800 --> 00:01:49,800
43 | A dragon.
44 |
45 | 11
46 | 00:01:50,800 --> 00:01:59,800
47 | A dangerous quest for a lone hunter.
48 |
49 | 12
50 | 00:02:32,950 --> 00:02:35,870
51 | I've been alone for
52 | as long as I can remember.
53 |
54 | 13
55 | 00:03:27,250 --> 00:03:30,500
56 | We're almost done. Shhh...
57 |
58 | 14
59 | 00:03:30,750 --> 00:03:33,500
60 | Hey, sit still.
61 |
62 | 15
63 | 00:03:48,250 --> 00:03:52,250
64 | Good night, Scales.
65 |
66 | 16
67 | 00:04:10,350 --> 00:04:13,850
68 | Get him, Scales! Come on!
69 |
70 | 17
71 | 00:04:25,250 --> 00:04:28,250
72 | Scales?
73 |
74 | 18
75 | 00:05:04,000 --> 00:05:07,500
76 | Yeah! Come on!
77 |
78 | 19
79 | 00:05:38,750 --> 00:05:42,000
80 | Scales!
81 |
82 | 20
83 | 00:07:25,850 --> 00:07:27,500
84 | I have failed.
85 |
86 | 21
87 | 00:07:32,800 --> 00:07:36,500
88 | You've only failed to see...
89 |
90 | 22
91 | 00:07:37,800 --> 00:07:40,500
92 | These are dragon lands, Sintel.
93 |
94 | 23
95 | 00:07:40,850 --> 00:07:44,000
96 | You are closer than you know.
97 |
98 | 24
99 | 00:09:17,600 --> 00:09:19,500
100 | Scales!
101 |
102 | 25
103 | 00:10:21,600 --> 00:10:24,000
104 | Scales?
105 |
106 | 26
107 | 00:10:26,200 --> 00:10:29,800
108 | Scales...
109 |
110 |
--------------------------------------------------------------------------------
/5-Captions/CAPTIONS_ru.srt:
--------------------------------------------------------------------------------
1 | 1
2 | 00:00:10,250 --> 00:00:19,500
3 | О! Это копьё с темным прошлым.
4 |
5 | 2
6 | 00:00:20,250 --> 00:00:29,500
7 | Его лезвие выпило немало невинной крови.
8 |
9 | 3
10 | 00:00:30,250 --> 00:00:39,500
11 | Только глупец путешествует в одиночку,
12 | да еще без оружия.
13 |
14 | 4
15 | 00:00:40,250 --> 00:00:49,500
16 | Тебе повезло, что осталась жива.
17 |
18 | 5
19 | 00:00:50,250 --> 00:00:59,500
20 | Спасибо.
21 |
22 | 6
23 | 00:01:00,500 --> 00:01:09,000
24 | Раскажи,
25 |
26 | 7
27 | 00:01:10,500 --> 00:01:19,000
28 | что привело тебя в страну
29 | хранителей?
30 |
31 | 8
32 | 00:01:20,500 --> 00:01:29,000
33 | Я ищу кое кого.
34 |
35 | 9
36 | 00:01:30,500 --> 00:01:39,000
37 | Кого-то очень дорогого?
38 | Родственную душу?
39 |
40 | 10
41 | 00:01:40,500 --> 00:01:49,000
42 | Дракона.
43 |
44 | 11
45 | 00:01:50,500 --> 00:01:59,000
46 | Опасное занятие для одиночки.
47 |
48 | 12
49 | 00:02:32,950 --> 00:02:35,870
50 | Сколько себя помню,
51 | я всегда была одна.
52 |
53 | 13
54 | 00:03:27,250 --> 00:03:30,500
55 | Уже почти всё. Шшшш...
56 |
57 | 14
58 | 00:03:30,750 --> 00:03:33,500
59 | Спокойно!
60 |
61 | 15
62 | 00:03:48,250 --> 00:03:52,250
63 | Спокойной ночи, Чешуйчик.
64 |
65 | 16
66 | 00:04:10,350 --> 00:04:13,850
67 | Взять, Чешуйчик! Давай!
68 |
69 | 17
70 | 00:04:25,250 --> 00:04:28,250
71 | Чешуйчик?
72 |
73 | 18
74 | 00:05:04,000 --> 00:05:07,500
75 | Да! Давай!
76 |
77 | 19
78 | 00:05:38,750 --> 00:05:42,000
79 | Чешуйчик!
80 |
81 | 20
82 | 00:07:25,850 --> 00:07:27,500
83 | У меня ничего не получилось.
84 |
85 | 21
86 | 00:07:32,800 --> 00:07:36,500
87 | Не получилось только понять,
88 |
89 | 22
90 | 00:07:37,800 --> 00:07:40,500
91 | что ты дошла до земли драконов, Синтэль.
92 |
93 | 23
94 | 00:07:40,850 --> 00:07:44,000
95 | И теперь ближе к цели,
96 | чем ты думаешь.
97 |
98 | 24
99 | 00:09:17,600 --> 00:09:19,500
100 | Чешуйчик!
101 |
102 | 25
103 | 00:10:21,600 --> 00:10:24,000
104 | Чешуйчик?
105 |
106 | 26
107 | 00:10:26,200 --> 00:10:29,800
108 | Чешуйчик...
109 |
--------------------------------------------------------------------------------
/5-Captions/README.md:
--------------------------------------------------------------------------------
1 | # Module 5: Working with Captions
2 |
3 | In this module you will modify one of the outputs created in our base job by adding captions from a side-car SRT file. SRT files are structured text files that contain subtitle information, which includes the sequential number of subtitles, start and end timecode, and subtitle text. The new job structure will look like this:
4 |
5 | 
6 |
7 | You might notice that the captions in this module don't match with the video. These captions came from a different source video.
8 |
9 | **TRIVIA QUESTION:**
10 |
11 | - What [Blender Foundation Open Movie Project](https://www.blender.org/features/projects/) do the captions we added in this module come from?
12 |
13 | **EXTRA CREDIT**
14 |
15 | - After you finish the module try creating your own captions for the VANLIFE video. Hint: You'll need to edit the captions files in this folder.
16 |
17 | ## Prerequisites
18 |
19 | You need to have access to MediaConvert and S3 to complete this module.
20 |
21 | You need the following resources created in module 1:
22 | * **MediaConvertRole** - the role created to give permission for MediaConvert to access resources in your account.
23 | * **MediaBucket** - the bucket created to store outputs from MediaConvert.
24 | * **MediaConvert job from module 2 or later** - We will start with this job and modify it in this module.
25 |
26 | If you used CloudFormation to configure resources, you will find the values names of MediaConvertRole and MediaBucket in the Outputs of the Stack.
27 |
28 |
29 | ## 1. Modify the MP4 output to include captions
30 |
31 | #### Duplicate the job from the previous module
32 |
33 | 1. Open the MediaConvert console for the region you are completing the lab in (US-West-Oregon). https://us-west-2.console.aws.amazon.com/mediaconvert/home?region=us-west-2#/welcome
34 | 1. Select **Jobs** from the side bar menu.
35 | 1. Find the job you created in the last module and click on the Job Id link to open the **Job details** page.
36 | 1. Select **Duplicate**
37 |
38 | #### Burn in captions to the MP4 output
39 |
40 | 1. Select **Input 1** from the Input section of the MediaConvert menu side bar to open the **Input settings** form.
41 | 1. Scroll down to the **Captions selectors** panel and select **Add caption selector** from the side bar.
42 | 1. Select **SRT** from the **Source** dropdown for Caption Selector 1
43 | 1. Enter `s3://rodeolabz-us-west-2/vodconsole/CAPTIONS_en.srt` in the **Source file** box.
44 |
45 | 
46 |
47 | 1. Select **Output 1** of the **MP4 - File Group** in the Output Groups section of the MediaConvert menu side bar to open the **Output settings** form for Output 1.
48 | 1. Select **Add captions** from the top of the **Stream settings** panel.
49 | 1. Make sure **Captions 1** is selected on the side bar of the **Stream settings** panel.
50 | 1. Select **Captions selector 1** in the **Captions source** drop down.
51 | 1. Select **Burn In** in the **Destination type** drop down.
52 | 1. Leave the rest of the settings as the default.
53 |
54 | 
55 |
56 | #### Create the job
57 |
58 | 1. Scroll to the bottom of the page and select **Create**
59 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job details** page.
60 |
61 | ### 2. Play the MP4
62 |
63 | To play the videos, you will use the S3 HTTPS resource **Link** on the videos S3 object **Overview** page.
64 |
65 | 
66 |
67 |
68 | #### MP4s
69 |
70 | The MP4 output is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/MP4/VANLIFE.mp4
71 |
72 | You can play the MP4 using:
73 | * Chrome by clicking on the **Link** for the object.
74 | * **JW Player Stream Tester** by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
75 |
76 | #### Video preview
77 |
78 | The MP4 output should be modified with captions. The HLS and Thumbnails outputs should be unchanged.
79 |
80 | 
81 |
82 | ## Completion
83 |
84 | In this module you added burned in captions to the MP4 output of you MediaConvert job. Hopefully, you also had a chance to play with captions metadata by making your own captions for the VANLIFE video. **Did you guess the name of the Blender Foundation Open Movie Project the captions came from?**
85 |
86 | Next module: [**Working with Embedded Input Metadata**](../6-EmbeddedMetadata/README.md)
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/5-Captions/TranscribeCaptions.md:
--------------------------------------------------------------------------------
1 | # MediaConvert Captions using Amazon Transcribe
2 |
3 | In this module we will create an SRT (SubRip Subtitle files) captions file that we will use to add caption to a MediaConvert job output. SRT files are structured text files that contain subtitle information, which includes the sequential number of subtitles, start and end timecode, and subtitle text. To create our SRT file, we will take an MP4 file and have Amazon Transcribe create a transcript out of that file. We will run a script that uses scripts from [this GitHub repo](https://github.com/aws-samples/aws-transcribe-captioning-tools/tree/master/tools) against the transcript to generate the SRT file. We will then modify one of the outputs created in our base job by adding captions using our generated side-car SRT file.
4 |
5 | ## Prerequisites
6 |
7 | 1. You need to have access to MediaConvert and S3 to complete this module.
8 |
9 | 1. You need the following resources created in [module 1](../1-IAMandS3/README.md):
10 | * **MediaConvertRole** - the role created to give permission for MediaConvert to access resources in your account.
11 | * **MediaBucket** - the bucket created to store outputs from MediaConvert.
12 | * **MediaConvert job from module 2 or later** - We will start with this job and modify it in this module.
13 |
14 | ## Create a captions file
15 |
16 | ### Run a Transcribe job
17 | 1. From AWS console, navigate to Amazon Transcribe.
18 | 1. From the **Transcription jobs** page, click on **Create job** button.
19 | 1. Under **Job Settings**, provide a name like `MyTranscriptionJob`.
20 | 1. Under **Input Data**, enter this URL `s3://rodeolabz-us-west-2/vodconsole/MediaConvert_Explained.mp4` for the **Input file location on S3**.
21 | 1. Under **Format**, select **MP4**.
22 | 1. Under **Output Data**, select **Customer Specified** and provide the bucket name of the bucket you created in Module 1.
23 |
24 | 
25 | 1. Take the defaults for the rest, and click on **Create** button.
26 | 1. After job completes, click on the output URL under Job details (`https://s3.us-west-2.amazonaws.com//MyTranscriptionJob.json`) that got written by the job.
27 | 1. Download the file and inspect it with a text editor.
28 |
29 | ### Re-run Transcribe job with custom vocabulary
30 | You probably noticed that certain words like `Media Convert` and `trance coding` aren't quite what we want in the transcript. We can fix this by providing a custom vocabulary and re-running our Transcribe job.
31 | 1. Create a local text file on your machine that has these words, one word per line.
32 | `MediaConvert`
33 |
34 | `transcoding`
35 | 1. Save the file on your Desktop, or somewhere accessible, and name it something like `customvocab.txt`.
36 | 1. From the Transcribe console, click on **Custom Vocabulary** on the left hand side navigation.
37 | 1. Click on the **Create Vocabulary** button.
38 | 1. Give it a name like `MediaConvertVocabulary`.
39 | 1. Click on **Choose file** and upload the local text your created (customvocab.txt).
40 |
41 | 
42 |
43 | 1. Once the vocabulary is in a **Ready** state (it will take a few minutes), click on **Transcription jobs** on the left hand side navigation.
44 | 1. Select the job you created earlier, and click on the **Copy** button.
45 | 1. Under **Input Data**, select the vocabulary you created under **Custom vocabulary** and **Create** the job.
46 | 1. After job completes, navigate to the S3 console and go into your bucket.
47 | 1. Select the JSON output (eg. `MyTranscriptionJob-copy.json`) that got written by the job.
48 | 1. Download the file and inspect it with a text editor. The incorrect words should now be showing up correctly.
49 |
50 | ### Process the transcript output
51 | Once the Transcribe job is completed, we can process the JSON output to create the SRT caption file.
52 |
53 | 1. From AWS console, navigate to Lambda.
54 | 1. Click on the **Create Function** button.
55 | 1. Enter a Function name like `CreateSRT`.
56 | 1. Choose **Python 3.7** from the **Runtime** dropdown.
57 | 1. Click on **Create function** button.
58 | 1. Scroll down to the **Execution role** section.
59 | 1. Click on the **View the CreateSRT-role** link. This will take you to the IAM console.
60 |
61 | 
62 | 1. Under **Permissions**, click on the **Attach Policies** button.
63 | 1. Search for `AmazonS3FullAccess` and select the policy.
64 | 1. Click on **Attach policy**. This will give Lambda read and write access to your bucket.
65 |
66 | 
67 | 1. Close the IAM console tab, and go back to the **Lambda** console.
68 | 1. Scroll up to the **Function code** section.
69 | 1. Under **Code entry type**, select **Upload a file from Amazon S3**.
70 | 1. For the **Amazon S3 link URL**, enter `https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/createSRT.zip`
71 | 1. Click on **Save** button to save the function.
72 | 1. Scroll down and edit the following variables from the lambda_function as needed:
73 | * **bucket** - set to your bucket name
74 | * **transcription_job** - set to the output name of your transcription job from Transcribe
75 | 1. Click on **Save** button to save your changes.
76 | 1. Click on the **Select a test event** dropdown.
77 | 1. Select **Configure test events**.
78 | 1. Give the event a name like `TestFunction`.
79 | 1. Click on **Create**.
80 | 1. Back on the function window, Click on the **Test** button. This will run our Lambda function.
81 | 1. Navigate back to your S3 bucket. Confirm that a file named `CAPTIONS_en.srt` has been created. You may download and open the file with a text editor to inspect the output.
82 |
83 | ## Modify the MP4 output to include captions
84 |
85 | ### Duplicate the job from the previous module
86 |
87 | 1. Open the MediaConvert console.
88 | 1. Select **Jobs** from the side bar menu.
89 | 1. Find the first job you created in the last module (only has an MP4 output) and click on the Job Id link to open the **Job details** page.
90 | 1. Select **Duplicate**.
91 |
92 | ### Burn in captions to the MP4 output
93 |
94 | 1. Select **Input 1** from the Input section of the MediaConvert menu side bar to open the **Input settings** form.
95 | 1. Point your input to `s3://rodeolabz-us-west-2/vodconsole/MediaConvert_Explained.mp4`
96 | 1. Scroll down to the **Captions selectors** panel and click on the **Add caption selector** button.
97 | 1. Select **SRT** from the **Source** dropdown for Caption Selector 1.
98 | 1. Enter the link to the SRT file you created `s3:///CAPTIONS_en.srt` in the **Source file** box. Replace _yourbucket_ with name of your bucket.
99 |
100 | 
101 |
102 | 1. Under **Output Groups**, click on **File Group - MP4**.
103 | 1. Update the **Destination** to `s3:///assets/EMCEXPLAINED/MP4/`, replacing _yourbucket_ with your bucket.
104 |
105 | 1. Select **Output 1** of the **MP4 - File Group** in the Output Groups section of the MediaConvert menu side bar to open the **Output settings** form for Output 1.
106 | 1. Select **Add captions** from the top of the **Encoding settings** panel.
107 | 1. Make sure **Captions 1** is selected on the side bar of the **Stream settings** panel.
108 | 1. Select **Captions selector 1** in the **Captions source** drop down.
109 | 1. Select **Burn In** in the **Destination type** drop down.
110 | 1. Set **Font size(pt)** to `20`.
111 | 1. Leave the rest of the settings as the default.
112 |
113 | 
114 |
115 | 1. Scroll to the bottom of the page and select **Create**
116 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job details** page.
117 |
118 | ### Play the MP4
119 |
120 | To play the videos, you will use the S3 HTTPS resource **Link** on the videos S3 object **Overview** page.
121 | 
122 |
--------------------------------------------------------------------------------
/6-EmbeddedMetadata/README.md:
--------------------------------------------------------------------------------
1 | # Module 6: Working with embedded input metadata
2 |
3 | The captions you added in the previous module are an example of _side-car_ metadata. That is, metadata that is stored separately from the video. Videos can also have _embedded metadata_ that is stored in the video package itself. In this module, we will look at some common examples of embedded metadata including Ad markers, embedded captions, and multi-language audio tracks. We will use Ad markers as an example to show how to work with embedded metadata in the video conversion workflow.
4 |
5 | The video you have been working with contains 2 language tracks and SCTE35 Ad Markers. Ad markers show where ads should be located in a video. Ad marker blanking inserts a visible _slate_ into the slot that is reserved for an Ad. Ad can be inserted on playout using a packager such as AWS Elemental MediaPackage.
6 |
7 | ## Prerequisites
8 |
9 | You need to have access to MediaConvert and S3 to complete this module.
10 |
11 | You need the following resources created in module 1:
12 | * **MediaConvertRole** - the role created to give permission for MediaConvert to access resources in your account.
13 | * **MediaBucket** - the bucket created to store outputs from MediaConvert.
14 | * **MediaConvert job from module 2 or later** - We will start with this job and modify it in this module.
15 |
16 | If you used CloudFormation to configure resources, you will find the values names of MediaConvertRole and MediaBucket in the Outputs of the Stack.
17 |
18 | ## 1. Modify a MediaConvert job to detect SCTE35 markers in the input and blank out embedded Ads
19 |
20 | Create a duplicate of the job you created in the last module. Modify the job to detect add markers and replace any embedded Ads with a slate image. The new job will have the following structure:
21 |
22 | 
23 |
24 |
25 | ### Detailed instructions
26 |
27 | #### Duplicate the job from the previous module
28 |
29 | 1. Open the MediaConvert console for the region you are completing the lab in (US-West-Oregon). https://us-west-2.console.aws.amazon.com/mediaconvert/home?region=us-west-2#/welcome
30 | 1. Select **Jobs** from the side bar menu.
31 | 1. Find the job you created in the last module and click on the Job Id link to open the **Job details** page.
32 | 1. Select **Duplicate**
33 | 1. Make sure the input video path for **Input 1** is set to: `s3://rodeolabz-us-west-2/vodconsole/VANLIFE.m2ts`. This video has been created with SCTE-35 Ad Markers inserted.
34 |
35 | #### Enable SCTE35 Ad availability blanking
36 |
37 | 1. Scroll down to the bottom of the MediaConvert side bar menu and select **Settings** from the **Job settings** section.
38 | 1. Scroll down to the **Global processors** panel and toggle the **Ad avail blanking** switch to the **on** position.
39 | 1. Enter `s3://rodeolabz-us-west-2/vodconsole/SLATE.png` in the **Blanking image** box.
40 |
41 | #### Create the job
42 |
43 | 1. Scroll to the bottom of the page and select **Create**
44 | 1. Wait for the job to complete. Monitor the status of the job by refreshing the **Job detail** page.
45 |
46 | ## 2. Play the videos
47 |
48 | Ad markers will now show up in all of the video outputs.
49 |
50 | To play the videos, you will use the S3 HTTPS resource **Link** on the videos S3 object **Overview** page.
51 |
52 | 
53 |
54 |
55 | #### MP4s
56 |
57 | The MP4 output is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/MP4/VANLIFE.mp4
58 |
59 | You can play the MP4 using:
60 | * Chrome by clicking on the **Link** for the object.
61 | * **JW Player Stream Tester** by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
62 |
63 | #### HLS
64 |
65 | The HLS manifest file is located in your ouput s3 bucket in the object: s3://YOUR-MediaBucket/assets/VANLIFE/HLS/VANLIFE.m3u8
66 |
67 | You can play the HLS using:
68 | * Safari browser by clicking on the **Link** for the object.
69 | * **JW Player Stream Tester** - by copying the link for the object and inputing it to the player. https://developer.jwplayer.com/tools/stream-tester/
70 |
71 | #### Video preview
72 |
73 | The MP4, HLS and Thumbnails will have one 10 second Ad marker slate image inserted starting 6 seconds into the video.
74 |
75 | 
76 |
77 | ## Completion
78 |
79 | In this module you learned about embedded metadata in file based video conversion workflows. In the next module, you will switch gears from the functionality of mediaconvert and focus on automation by creating a lambda function that starts a new MediaConvert job whenever a new file is written to an S3 bucket.
80 |
81 | Next module: [**Automating Jobs with Lambda**](../7-MediaConvertJobLambda/README.md)
82 |
83 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/AutomatingWithEDL.md:
--------------------------------------------------------------------------------
1 | # Using an Edit Decision List(EDL) to Automate MediaConvert Jobs
2 |
3 | In this module, you'll be using Amazon S3 events and a Lambda to automatically trigger AWS Elemental MediaConvert jobs. But instead of keying off of an input video file that's been placed in an S3 bucket, an Edit Decision List (EDL) file placed in S3 will trigger a Lambda that in turn will kick off a MediaConvert encoding job. The files to be used for transcoding are expected to also be placed in the same S3 bucket and path as the EDL file.
4 |
5 | ## Background on EDLs
6 | An EDL is used in the post-production process of film and video editing. It contains an ordered list of video and corresponding timecode information on where each video should be clipped to put together the final cut of the film.
7 |
8 | Below is a sample EDL which we will be using in this tutorial. It's been generated by Adobe Premiere, a video editing software. This EDL is based on the CMX3600 format, which supports relatively simple editing decisions. We will mostly be using the cut information (signified by **C**, in the 4th column of the EDL sample below) which MediaConvert supports through its input clipping feature. Information about the CMX3600 format relevant to the sample below can be found in pages 10 - 14 of the [specification](http://xmil.biz/EDL-X/CMX3600.pdf).
9 |
10 | 1 TITLE: llama-ad-llama
11 | 2 FCM: NON-DROP FRAME
12 | 3
13 | 4 001 AX V C 00:00:00:00 00:00:45:00 00:00:00:00 00:00:45:00
14 | 5 * FROM CLIP NAME: llama_drama.mp4
15 | 6
16 | 7 002 AX V C 00:00:00:00 00:00:15:00 00:00:45:00 00:01:00:00
17 | 8 * FROM CLIP NAME: ad_caribbean.mp4
18 | 9
19 | 10 003 AX V C 00:00:45:00 00:01:30:00 00:01:00:00 00:01:45:00
20 | 11 * FROM CLIP NAME: llama_drama.mp4
21 |
22 | The lines starting with a numerical ID (lines 4, 7, and 10) contain the clipping information for the asset specified in the very next line. Columns 5 and 6 are the start and end timecode, respectively, for the input clipping. This information will be used to put together a MediaConvert job that will stitch all the input videos together at the specified clip times. Based on the data in the above EDL, we should end up with a video that has the first 45 seconds of the llama_drama video, followed by a 15-second ad video, and then the last 45 seconds of the same llama_drama clip.
23 |
24 | The Lambda function you will create will be invoked each time a user uploads an EDL and the input videos contained in the EDL. The lambda will create a MediaConvert job to produce several outputs:
25 |
26 | - An Apple HLS adaptive bitrate stream for playout on multiple sized devices and varying bandwidths.
27 | - An MP4 stream
28 | - Thumbnails for use in websites to show a preview of the video at a point in time.
29 |
30 | ## Prerequisites
31 | This module assumes you have completed the following previous modules:
32 | * [1-IAMandS3](../1-IAMandS3/README.md) - note the output S3 bucket and IAM role ARN created in this module
33 | * [7-MediaConvertJobLambda](README.md) - note the Lambda role ARN created in this module
34 |
35 |
36 | ### Create an Amazon S3 bucket
37 |
38 | Use the console to create a bucket where you're going to upload your video input files, as well as your EDL file. Remember that a bucket's name must be globally unique across all regions and customers. We recommend using a name like `edl-watchfolder-firstname-lastname`. If you get an error that your bucket name already exists, try adding additional numbers or characters until you find an unused name.
39 |
40 | 1. In the AWS Management Console, choose **Services** then select **S3** under Storage.
41 | 1. Choose **+Create Bucket**
42 | 1. Provide a globally unique name for your bucket such as `edl-watchfolder-firstname-lastname`.
43 | 1. Select the Region you've chosen to use for this workshop from the dropdown.
44 | 1. Choose **Create** in the lower left of the dialog without selecting a bucket to copy settings from.
45 |
46 |
47 | ### Create the Lambda Function
48 | In this step you'll build the core function that will process the EDL and create a MediaConvert job using the python SDK. The Lambda function will respond to putObject events in your S3 source bucket. Whenever an EDL file is added, the Lambda will start a MediaConvert job, but it won't wait for the job to complete.
49 |
50 | #### Step-by-step instructions
51 |
52 | 1. From the AWS console, choose **Services** then select **Lambda**.
53 | 1. Click on **Create function**.
54 | 1. In the **Basic Information** section, enter `EDLMediaConvert` in the **Function name** field.
55 | 1. Select **Python 3.7** for the **Runtime**.
56 | 1. Expand the **Choose or create an execution role** section in the **Permissions** section.
57 | 1. Select **Use an existing role**.
58 | 1. Select `VODLambdaRole` from the **Existing Role** dropdown. This should be the same role that you created in an [earlier module](README.md#vod-lambda-role).
59 | 1. Click on **Create function**.
60 |
61 | 
62 |
63 | 1. On the Configuration tab of the `EDLMediaConvert` page, in the **function code** panel:
64 |
65 | a. Select **Upload a file from Amazon S3** for the **Code entry type**
66 |
67 | b. Enter the following for the URL: `https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/input_clip.zip`.
68 |
69 | _Note that this zip file is simply the [input_clip_convert.py script](input_clip_convert.py) and the [input clip job JSON](input_clipping_job.json) file provided in this repo that you could zip up yourself, if desired. We've hosted the zip file for convenience._
70 |
71 | c. Enter `input_clip_convert.lambda_handler` for the **Handler** field.
72 |
73 | 
74 |
75 | 1. On the **Environment Variables** section, enter the following keys and values:
76 |
77 | * DestinationBucket = vod-lastname or whatever you named your media output bucket in [1-IAMandS3](../1-IAMandS3/README.md)
78 | * MediaConvertRole = `arn:aws:iam::ACCOUNT NUMBER:role/VODMediaConvertRole` created in [7-MediaConvertJobLambda](README.md)
79 | * Application = VOD
80 |
81 | 
82 |
83 | 1. On the **Basic Settings** panel, set **Timeout** to 2 minutes.
84 |
85 | 1. Scroll back to the top of the page and click on the **Save** button.
86 |
87 |
88 | ### Create a S3 PutItem Event Trigger for your Convert lambda
89 |
90 | In the previous step, you built a lambda function that will convert a video in response to an S3 PutItem event. Now it's time to hook up the Lambda trigger to the EDL watchfolder S3 bucket.
91 |
92 | #### Step-by-step instructions
93 |
94 | 1. In the **Configuration->Designer** panel of the EDLMediaConvert function, click on **Add trigger** button.
95 | 1. Select **S3** from the **Trigger configuration** dropdown.
96 | 1. Select `edl-watchfolder-firstname-lastname` or the name you used for the watchfolder bucket you created earlier in this module for the **Bucket**.
97 | 1. Select **PUT** for the **Event type**.
98 |
99 | 
100 | 1. Enter `.edl` for the **Suffix**.
101 | 1. Click the **Add** button.
102 |
103 | 
104 |
105 | ### Test the Automation
106 | Now that the Lambda function has been created, and the S3 trigger has been put in place, you can test the automation workflow by uploading the video inputs first, and then the EDL file to your watchfolder S3 bucket.
107 |
108 | 1. Download the following video inputs and EDL file on to your local machine:
109 | * [llama_drama.mp4](https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/llama_drama.mp4)
110 | * [ad_caribbean.mp4](https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/ad_caribbean.mp4)
111 | * [llama-ad-llama.edl](https://rodeolabz-us-west-2.s3-us-west-2.amazonaws.com/vodconsole/llama-ad-llama.edl)
112 |
113 |
114 | 1. From S3 console, click on the EDL watchfolder S3 bucket you created earlier (`edl-watchfolder-firstname-lastname`).
115 | 1. Select **Upload** and then choose the files `llama_drama.mp4` and `ad_carribean.mp4` that you just downloaded.
116 | 1. Once the input files have been uploaded, select **Upload** again and this time select `llama-ad-llama.edl`.
117 | 1. Open a new browser tab and navigate to the MediaConvert console.
118 | 1. From the MediaConvert jobs page, you should find a job with `llama_drama.mp4` as the **First input file name**.
119 |
120 | 
121 | 1. Click on the job ID to show the **Job Summary**. It should show 3 inputs: llama_drama.mp4, ad_caribbean.mp4, and llama_drama.mp4.
122 |
123 | 
124 | 1. Click on the MP4 or the HLS output link. Play the output video by clicking on the llama-ad-llama output. Verify that the first 45 seconds of llama_drama plays, followed by a 15-second ad, then the last 45 seconds of llama_drama.
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/WatchFolder.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates an S3 watchfolder and automated video conversion workflow the VODonline website
6 |
7 | Parameters:
8 | MediaBucket:
9 | Type: String
10 | Description: The name for the bucket to store outputs, e.g. 'vod-yourname.'
11 | MediaConvertRoleArn:
12 | Type: String
13 | Description: The ARN for the role previously to pass to MediaConvert so it can access to resources in your account
14 |
15 | Mappings:
16 | SourceCode:
17 | General:
18 | S3Bucket: "rodeolabz"
19 | KeyPrefix: "vodconsole"
20 |
21 | Resources:
22 |
23 | LambdaRole:
24 | Type: AWS::IAM::Role
25 | Properties:
26 | RoleName: !Sub "${AWS::StackName}-LambdaRole"
27 | AssumeRolePolicyDocument:
28 | Version: 2012-10-17
29 | Statement:
30 | -
31 | Effect: Allow
32 | Principal:
33 | Service:
34 | - lambda.amazonaws.com
35 | Action:
36 | - sts:AssumeRole
37 | Policies:
38 | -
39 | PolicyName: !Sub "${AWS::StackName}-LambdaPolicy"
40 | PolicyDocument:
41 | Version: 2012-10-17
42 | Statement:
43 | -
44 | Sid: Logging
45 | Effect: Allow
46 | Action:
47 | - "logs:CreateLogGroup"
48 | - "logs:CreateLogStream"
49 | - "logs:PutLogEvents"
50 | Resource: "*"
51 | -
52 | Sid: PassRole
53 | Effect: Allow
54 | Action:
55 | - iam:PassRole
56 | Resource:
57 | - !Ref MediaConvertRoleArn
58 | - Sid: MediaConvertService
59 | Effect: Allow
60 | Action:
61 | - "mediaconvert:*"
62 | Resource:
63 | - "*"
64 |
65 | S3InvokeLambda:
66 | Type: AWS::Lambda::Permission
67 | Properties:
68 | FunctionName: !GetAtt LambdaConvert.Arn
69 | Action: lambda:InvokeFunction
70 | Principal: s3.amazonaws.com
71 | SourceAccount: !Ref AWS::AccountId
72 |
73 | WatchFolder:
74 | Type: AWS::S3::Bucket
75 | Properties:
76 | LifecycleConfiguration:
77 | Rules:
78 | - Id: ExpireRule
79 | Status: Enabled
80 | ExpirationInDays: '7'
81 | NotificationConfiguration:
82 | LambdaConfigurations:
83 | -
84 | Function: !GetAtt LambdaConvert.Arn
85 | Event: "s3:ObjectCreated:*"
86 | DeletionPolicy: Retain
87 |
88 | LambdaConvert:
89 | Type: AWS::Lambda::Function
90 | Properties:
91 | FunctionName: !Sub ${AWS::StackName}-convert
92 | Description: Converts input video
93 | Handler: convert.handler
94 | Role: !GetAtt LambdaRole.Arn
95 | Code:
96 | S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], !Ref "AWS::Region"]]
97 | S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "lambda.zip"]]
98 | Environment:
99 | Variables:
100 | DestinationBucket: !Ref MediaBucket
101 | MediaConvertRole: !Ref MediaConvertRoleArn
102 | Runtime: python3.7
103 | Timeout: 120
104 |
105 | Outputs:
106 | WatchFolderBucket:
107 | Value: !Ref WatchFolder
108 | LambdaRoleArn:
109 | Value: !GetAtt LambdaRole.Arn
110 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/convert.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import glob
4 | import json
5 | import os
6 | import uuid
7 | import boto3
8 | import datetime
9 | import random
10 |
11 | from botocore.client import ClientError
12 |
13 | def handler(event, context):
14 |
15 | assetID = str(uuid.uuid4())
16 | sourceS3Bucket = event['Records'][0]['s3']['bucket']['name']
17 | sourceS3Key = event['Records'][0]['s3']['object']['key']
18 | sourceS3 = 's3://'+ sourceS3Bucket + '/' + sourceS3Key
19 | sourceS3Basename = os.path.splitext(os.path.basename(sourceS3))[0]
20 | destinationS3 = 's3://' + os.environ['DestinationBucket']
21 | destinationS3basename = os.path.splitext(os.path.basename(destinationS3))[0]
22 | mediaConvertRole = os.environ['MediaConvertRole']
23 | region = os.environ['AWS_DEFAULT_REGION']
24 | statusCode = 200
25 | body = {}
26 |
27 | # Use MediaConvert SDK UserMetadata to tag jobs with the assetID
28 | # Events from MediaConvert will have the assetID in UserMedata
29 | jobMetadata = {'assetID': assetID}
30 |
31 | print (json.dumps(event))
32 |
33 | try:
34 | # Job settings are in the lambda zip file in the current working directory
35 | with open('job.json') as json_data:
36 | jobSettings = json.load(json_data)
37 | print(jobSettings)
38 |
39 | # get the account-specific mediaconvert endpoint for this region
40 | mc_client = boto3.client('mediaconvert', region_name=region)
41 | endpoints = mc_client.describe_endpoints()
42 |
43 | # add the account-specific endpoint to the client session
44 | client = boto3.client('mediaconvert', region_name=region, endpoint_url=endpoints['Endpoints'][0]['Url'], verify=False)
45 |
46 | # Update the job settings with the source video from the S3 event and destination
47 | # paths for converted videos
48 | jobSettings['Inputs'][0]['FileInput'] = sourceS3
49 |
50 | S3KeyHLS = 'assets/' + assetID + '/HLS/' + sourceS3Basename
51 | jobSettings['OutputGroups'][0]['OutputGroupSettings']['HlsGroupSettings']['Destination'] \
52 | = destinationS3 + '/' + S3KeyHLS
53 |
54 | S3KeyWatermark = 'assets/' + assetID + '/MP4/' + sourceS3Basename
55 | jobSettings['OutputGroups'][1]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
56 | = destinationS3 + '/' + S3KeyWatermark
57 |
58 | S3KeyThumbnails = 'assets/' + assetID + '/Thumbnails/' + sourceS3Basename
59 | jobSettings['OutputGroups'][2]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
60 | = destinationS3 + '/' + S3KeyThumbnails
61 |
62 | print('jobSettings:')
63 | print(json.dumps(jobSettings))
64 |
65 | # Convert the video using AWS Elemental MediaConvert
66 | job = client.create_job(Role=mediaConvertRole, UserMetadata=jobMetadata, Settings=jobSettings)
67 | print (json.dumps(job, default=str))
68 |
69 | except Exception as e:
70 | print ('Exception: %s' % e)
71 | statusCode = 500
72 | raise
73 |
74 | finally:
75 | return {
76 | 'statusCode': statusCode,
77 | 'body': json.dumps(body),
78 | 'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'}
79 | }
80 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/input_clip.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/7-MediaConvertJobLambda/input_clip.zip
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/input_clip_convert.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import json
4 | import os
5 | import uuid
6 | import boto3
7 | import copy
8 |
9 | from botocore.client import ClientError
10 |
11 | def lambda_handler(event, context):
12 |
13 | assetID = str(uuid.uuid4())
14 | sourceS3Bucket = event['Records'][0]['s3']['bucket']['name']
15 | sourceS3Key = event['Records'][0]['s3']['object']['key']
16 |
17 | # this is the EDL file that got dropped in the bucket that triggered this Lambda
18 | sourceS3 = 's3://'+ sourceS3Bucket + '/' + sourceS3Key
19 | sourceS3Basename = "edl-title"
20 | destinationS3 = 's3://' + os.environ['DestinationBucket']
21 | destinationS3basename = os.path.splitext(os.path.basename(destinationS3))[0]
22 |
23 | mediaConvertRole = os.environ['MediaConvertRole']
24 | region = os.environ['AWS_DEFAULT_REGION']
25 | statusCode = 200
26 | body = {}
27 |
28 | # Use MediaConvert SDK UserMetadata to tag jobs with the assetID
29 | # Events from MediaConvert will have the assetID in UserMedata
30 | jobMetadata = {'assetID': assetID}
31 |
32 | print (json.dumps(event))
33 |
34 | Settings = {}
35 | Settings["Inputs"] = []
36 | VideoSettings = {
37 | "AudioSelectors": {
38 | "Audio Selector 1": {
39 | "Offset": 0,
40 | "DefaultSelection": "DEFAULT",
41 | "ProgramSelection": 1
42 | }
43 | },
44 | "VideoSelector": {
45 | "ColorSpace": "FOLLOW",
46 | "Rotate": "DEGREE_0"
47 | },
48 | "FilterEnable": "AUTO",
49 | "PsiControl": "USE_PSI",
50 | "FilterStrength": 0,
51 | "DeblockFilter": "DISABLED",
52 | "DenoiseFilter": "DISABLED",
53 | "TimecodeSource": "ZEROBASED",
54 | "FileInput": "s3://rodeolabz-us-west-2/reinvent2019/llama_drama.mp4",
55 | "InputClippings": []
56 | }
57 | s3 = boto3.resource('s3')
58 | s3.meta.client.download_file(sourceS3Bucket, sourceS3Key, '/tmp/this.edl')
59 |
60 | # process the EDL
61 | with open('/tmp/this.edl') as f:
62 | linelist = f.readlines()
63 | #first line is always title
64 | sourceS3Basename = linelist[0].split(":")[1].strip() # save the title
65 |
66 | iterlist = iter(linelist)
67 | done_looping = False
68 | while not done_looping:
69 | try:
70 | item = next(iterlist)
71 | except StopIteration:
72 | done_looping = True
73 | else:
74 | form_statement_list = item.split(' ')
75 | #if the first item is numeric, then this is the edit decision ID
76 | #and this line will have the cut information
77 | if form_statement_list[0].isnumeric():
78 | # this has the cut info
79 | cut_info_list = item.split(' ')
80 | print (cut_info_list)
81 | #4th item from end = start of input clip
82 | input_clip_start_time = cut_info_list[-4].strip()
83 |
84 | #3rd item from end = end of input clip
85 | input_clip_end_time = cut_info_list[-3].strip()
86 |
87 | # the very next line we expect to have the user note on asset/clip name
88 | # it's the first item from end of list after split
89 | # assumption: the asset/clips being used are in the same bucket/path that the EDL was written to
90 | clip_name = os.path.dirname(sourceS3) + "/" + next(iterlist).split(' ')[-1].strip()
91 | VideoSettings["InputClippings"] = [{"StartTimecode": input_clip_start_time, "EndTimecode": input_clip_end_time}]
92 | VideoSettings["FileInput"] = clip_name
93 | Settings["Inputs"].append(copy.deepcopy(VideoSettings))
94 | print(Settings)
95 |
96 | try:
97 | # Job settings are in the lambda zip file in the current working directory
98 | with open('input_clipping_job.json') as json_data:
99 | jobSettings = json.load(json_data)
100 | #print(jobSettings)
101 |
102 | # get the account-specific mediaconvert endpoint for this region
103 | mc_client = boto3.client('mediaconvert', region_name=region)
104 | endpoints = mc_client.describe_endpoints()
105 |
106 | # add the account-specific endpoint to the client session
107 | client = boto3.client('mediaconvert', region_name=region, endpoint_url=endpoints['Endpoints'][0]['Url'], verify=False)
108 |
109 | # Update the job settings with the source video from the S3 event and destination
110 | # paths for converted videos
111 | jobSettings['Inputs']=Settings['Inputs']
112 |
113 | S3KeyHLS = 'assets/' + assetID + '/HLS/' + sourceS3Basename
114 | jobSettings['OutputGroups'][0]['OutputGroupSettings']['HlsGroupSettings']['Destination'] \
115 | = destinationS3 + '/' + S3KeyHLS
116 |
117 | S3KeyWatermark = 'assets/' + assetID + '/MP4/' + sourceS3Basename
118 | jobSettings['OutputGroups'][1]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
119 | = destinationS3 + '/' + S3KeyWatermark
120 |
121 | S3KeyThumbnails = 'assets/' + assetID + '/Thumbnails/' + sourceS3Basename
122 | jobSettings['OutputGroups'][2]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
123 | = destinationS3 + '/' + S3KeyThumbnails
124 |
125 | print('jobSettings:')
126 | print(json.dumps(jobSettings))
127 |
128 | # Convert the video using AWS Elemental MediaConvert
129 | job = client.create_job(Role=mediaConvertRole, UserMetadata=jobMetadata, Settings=jobSettings)
130 | print (json.dumps(job, default=str))
131 |
132 | except Exception as e:
133 | print ('Exception: %s' % e)
134 | statusCode = 500
135 | raise
136 |
137 | finally:
138 | return {
139 | 'statusCode': statusCode,
140 | 'body': json.dumps(body),
141 | 'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'}
142 | }
143 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/lambda.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/7-MediaConvertJobLambda/lambda.zip
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/llama-ad-llama.edl:
--------------------------------------------------------------------------------
1 | TITLE: llama-ad-llama
2 | FCM: NON-DROP FRAME
3 |
4 | 001 AX V C 00:00:00:00 00:00:45:00 00:00:00:00 00:00:45:00
5 | * FROM CLIP NAME: llama_drama.mp4
6 |
7 | 002 AX V C 00:00:00:00 00:00:15:00 00:00:45:00 00:01:00:00
8 | * FROM CLIP NAME: ad_caribbean.mp4
9 |
10 | 003 AX V C 00:00:45:00 00:01:30:00 00:01:00:00 00:01:45:00
11 | * FROM CLIP NAME: llama_drama.mp4
12 |
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/test.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/7-MediaConvertJobLambda/test.mp4
--------------------------------------------------------------------------------
/7-MediaConvertJobLambda/ziplambda.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -v
2 |
3 | zip -r lambda.zip convert.py job.json
4 |
5 |
--------------------------------------------------------------------------------
/8-MediaConvertEvents/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 |
--------------------------------------------------------------------------------
/8-MediaConvertEvents/README-cloudformation.md:
--------------------------------------------------------------------------------
1 | ### Launching the Stack on AWS
2 |
3 | A CloudFormation template is provided for this module in the file `simple-events.yaml.yaml` to build the workflow automatically. Click **Launch Stack** to launch the template in your account in the region of your choice :
4 |
5 | Region| Launch
6 | ------|-----
7 | US East (N. Virginia) | [](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=emc-events&templateURL=https://s3.amazonaws.com/rodeolabz-us-east-1/vodtk/1b-mediaconvert-events/simple-events.yaml)
8 | US West (Oregon) | [](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=emc-events&templateURL=https://s3.amazonaws.com/rodeolabz-us-west-2/vodtk/1b-mediaconvert-events/simple-events.yaml)
9 |
10 |
11 | The information about the resources created is in the **Outputs** tab of the stack. Save this in a browser tab so you can use it later when you create other stacks and MediaConvert jobs.
12 |
13 | 
14 |
15 |
--------------------------------------------------------------------------------
/8-MediaConvertEvents/simple-events.yaml:
--------------------------------------------------------------------------------
1 | Parameters:
2 |
3 | EventTableTTL:
4 | Description: The amount of time in days that current event data is cached in Dynamodb tables
5 | Type: Number
6 | Default: 1
7 |
8 | Mappings:
9 | SourceCode:
10 | General:
11 | S3Bucket: "rodeolabz"
12 | KeyPrefix: "vodtk/1b-mediaconvert-events"
13 |
14 | Resources:
15 |
16 | EventTable:
17 | Type: AWS::DynamoDB::Table
18 | Properties:
19 | TableName: !Sub "${AWS::StackName}-EventTable"
20 | AttributeDefinitions:
21 | -
22 | AttributeName: id
23 | AttributeType: S
24 | -
25 | AttributeName: timestamp
26 | AttributeType: N
27 | -
28 | AttributeName: jobId
29 | AttributeType: S
30 | KeySchema:
31 | -
32 | AttributeName: id
33 | KeyType: HASH
34 | -
35 | AttributeName: timestamp
36 | KeyType: RANGE
37 | GlobalSecondaryIndexes:
38 | -
39 | IndexName: jobId-timestamp-index
40 | KeySchema:
41 | -
42 | AttributeName: jobId
43 | KeyType: HASH
44 | -
45 | AttributeName: timestamp
46 | KeyType: RANGE
47 | Projection:
48 | ProjectionType: ALL
49 | ProvisionedThroughput:
50 | ReadCapacityUnits: 5
51 | WriteCapacityUnits: 5
52 | ProvisionedThroughput:
53 | ReadCapacityUnits: 5
54 | WriteCapacityUnits: 5
55 | TimeToLiveSpecification:
56 | AttributeName: timestampTTL
57 | Enabled: true
58 |
59 | EventLambdaRole:
60 | Type: "AWS::IAM::Role"
61 | Properties:
62 | RoleName: !Sub "${AWS::StackName}-EventLambdaRole"
63 | AssumeRolePolicyDocument:
64 | Version: 2012-10-17
65 | Statement:
66 | -
67 | Effect: Allow
68 | Principal:
69 | Service:
70 | - lambda.amazonaws.com
71 | Action:
72 | - sts:AssumeRole
73 | Policies:
74 | -
75 | PolicyName: !Sub "${AWS::StackName}-EventLambdaPolicy"
76 | PolicyDocument:
77 | Statement:
78 | - Effect: Allow
79 | Action:
80 | - logs:CreateLogGroup
81 | - logs:CreateLogStream
82 | - logs:PutLogEvents
83 | Resource:
84 | - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/*"]]
85 | -
86 | Effect: Allow
87 | Action:
88 | - dynamodb:GetItem
89 | - dynamodb:PutItem
90 | - dynamodb:UpdateItem
91 | - dynamodb:Scan
92 | Resource:
93 | - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "EventTable"]]
94 |
95 | EventInvokeLambda:
96 | Type: AWS::Lambda::Permission
97 | Properties:
98 | FunctionName: !GetAtt EventCollectorLambda.Arn
99 | Action: lambda:InvokeFunction
100 | Principal: events.amazonaws.com
101 | SourceArn:
102 | Fn::GetAtt:
103 | - "AllMediaConvertEventRule"
104 | - "Arn"
105 |
106 | AllMediaConvertEventRule:
107 | Type: "AWS::Events::Rule"
108 | Properties:
109 | Description: catches MediaConvert Events
110 | State: "ENABLED"
111 | EventPattern:
112 | source:
113 | - "aws.mediaconvert"
114 | Targets:
115 | -
116 | Arn:
117 | Fn::GetAtt:
118 | - "EventCollectorLambda"
119 | - "Arn"
120 | Id: "TargetEventCollectorLambda"
121 |
122 | EventCollectorLambda:
123 | Type: AWS::Lambda::Function
124 | Properties:
125 | FunctionName: !Sub ${AWS::StackName}-EventCollector
126 | Description: Collect events, update status, make metrics
127 | Handler: simple_event_collector.lambda_handler
128 | Role: !GetAtt EventLambdaRole.Arn
129 | Code:
130 | S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], !Ref "AWS::Region"]]
131 | S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "lambda.zip"]]
132 | Environment:
133 | Variables:
134 | EventTable: !Ref EventTable
135 | EventTableTTL: !Ref EventTableTTL
136 | Runtime: python3.6
137 | Timeout: 120
138 |
139 | APILambdaRole:
140 | Type: "AWS::IAM::Role"
141 | Properties:
142 | RoleName: !Sub "${AWS::StackName}-APILambdaRole"
143 | AssumeRolePolicyDocument:
144 | Version: 2012-10-17
145 | Statement:
146 | -
147 | Effect: Allow
148 | Principal:
149 | Service:
150 | - lambda.amazonaws.com
151 | Action:
152 | - sts:AssumeRole
153 | Policies:
154 | -
155 | PolicyName: !Sub "${AWS::StackName}-APILambdaPolicy"
156 | PolicyDocument:
157 | Statement:
158 | - Effect: Allow
159 | Action:
160 | - logs:CreateLogGroup
161 | - logs:CreateLogStream
162 | - logs:PutLogEvents
163 | Resource:
164 | - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/*"]]
165 | -
166 | Effect: Allow
167 | Action:
168 | - dynamodb:GetItem
169 | - dynamodb:Scan
170 | - dynamodb:BatchGetItem
171 | - dynamodb:DescribeTable
172 | - dynamodb:GetItem
173 | - dynamodb:ListTables
174 | - dynamodb:Query
175 | - dynamodb:Scan
176 | - dynamodb:DescribeReservedCapacity
177 | - dynamodb:DescribeReservedCapacityOfferings
178 | - dynamodb:ListTagsOfResource
179 | - dynamodb:DescribeTimeToLive
180 | - dynamodb:DescribeLimits
181 | - dynamodb:ListGlobalTables
182 | Resource:
183 | - "*"
184 | # - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "JobTable"]]
185 | # - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "EventTable"]]
186 |
187 | Outputs:
188 | EventTable:
189 | Description: The name of the Table to get Event data from
190 | Value: !Ref EventTable
191 | APILambdaRoleArn:
192 | Description: The ARN of the role to use to grant APIs access to Dyanmodb tables
193 | Value: !GetAtt APILambdaRole.Arn
194 |
--------------------------------------------------------------------------------
/8-MediaConvertEvents/simple_event_collector.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | import boto3
3 | import datetime
4 | import json
5 | import time
6 | import decimal
7 | from botocore.client import ClientError
8 | from boto3 import resource
9 | from boto3.dynamodb.conditions import Key
10 | import os
11 | import traceback
12 |
13 | # Helper class to convert a DynamoDB item to JSON.
14 | class DecimalEncoder(json.JSONEncoder):
15 | def default(self, o):
16 | if isinstance(o, decimal.Decimal):
17 | if o % 1 > 0:
18 | return float(o)
19 | else:
20 | return int(o)
21 | return super(DecimalEncoder, self).default(o)
22 |
23 | DYNAMO_CLIENT = boto3.resource('dynamodb')
24 |
25 | def lambda_handler(event, context):
26 |
27 | print(json.dumps(event))
28 |
29 | job = {}
30 | tsevent = int(datetime.datetime.strptime(event["time"], "%Y-%m-%dT%H:%M:%SZ").timestamp())
31 |
32 | try:
33 | # Get environment variables set on the CloudFormation stack
34 | EVENTTABLE = os.environ['EventTable']
35 | EVENTTABLETTL = os.environ['EventTableTTL']
36 |
37 | EVENT_RETENTION_PERIOD = (3600 * 24 * int(EVENTTABLETTL))
38 |
39 | # add expirtation timestamp for dynamo and save the event in dynamo
40 | event["timestamp"] = tsevent
41 | event["timestampTTL"] = tsevent + EVENT_RETENTION_PERIOD
42 | event["jobId"] = event['detail']['jobId']
43 | s = json.dumps(event, cls=DecimalEncoder)
44 | event = json.loads(s, parse_float=decimal.Decimal)
45 | table = DYNAMO_CLIENT.Table(EVENTTABLE)
46 | response = table.put_item(Item = event)
47 | print(json.dumps(response, cls=DecimalEncoder))
48 |
49 | except Exception as e:
50 | print('An error occured {}'.format(e))
51 | traceback.print_stack()
52 | raise
53 | else:
54 | return True
--------------------------------------------------------------------------------
/9-MediainfoLambda/.gitignore:
--------------------------------------------------------------------------------
1 | mediainfo
2 | *.zip
3 | ziplambda.sh
--------------------------------------------------------------------------------
/9-MediainfoLambda/mediainfo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.6
2 | import boto3
3 | import datetime
4 | import json
5 | import time
6 | import decimal
7 | from botocore.client import ClientError
8 | from boto3 import resource
9 | from boto3.dynamodb.conditions import Key
10 | import logging
11 | import subprocess
12 | #import urllib
13 | from urllib.parse import urlparse
14 | import timecode
15 | from timecode import Timecode
16 | import xmltodict
17 | import logging
18 | import os
19 | import traceback
20 |
21 | # Helper class to convert a DynamoDB item to JSON.
22 | class DecimalEncoder(json.JSONEncoder):
23 | def default(self, o):
24 | if isinstance(o, decimal.Decimal):
25 | if o % 1 > 0:
26 | return float(o)
27 | else:
28 | return int(o)
29 | return super(DecimalEncoder, self).default(o)
30 |
31 | DYNAMO_CLIENT = boto3.resource('dynamodb')
32 |
33 | SIGNED_URL_EXPIRATION = 300
34 |
35 | logger = logging.getLogger('boto3')
36 | logger.setLevel(logging.INFO)
37 |
38 | def get_signed_url(expires_in, bucket, obj):
39 | """
40 | Generate a signed URL
41 | :param expires_in: URL Expiration time in seconds
42 | :param bucket:
43 | :param obj: S3 Key name
44 | :return: Signed URL
45 | """
46 | s3_cli = boto3.client("s3")
47 | presigned_url = s3_cli.generate_presigned_url('get_object', Params={'Bucket': bucket, 'Key': obj},
48 | ExpiresIn=expires_in)
49 | return presigned_url
50 |
51 | def lambda_handler(event, context):
52 |
53 | print(json.dumps(event))
54 |
55 | tsevent = int(datetime.datetime.strptime(event["time"], "%Y-%m-%dT%H:%M:%SZ").timestamp())
56 |
57 | try:
58 | # Get environment variables set on the CloudFormation stack
59 | MEDIAINFOTABLE = os.environ['MediainfoTable']
60 | MEDIAINFOTABLETTL = os.environ['MediainfoTableTTL']
61 |
62 | MEDIAINFO_RETENTION_PERIOD = (3600 * 24 * int(MEDIAINFOTABLETTL))
63 |
64 | # Loop through input videos in the event
65 | for input in event['detail']['inputDetails']:
66 | s3_path = input['uri']
67 | urlp = urlparse(s3_path)
68 | # Extract the Key and Bucket names for the inputs
69 | key = urlp[2]
70 | key = key.lstrip('/')
71 | bucket = urlp[1]
72 |
73 | signed_url = get_signed_url(SIGNED_URL_EXPIRATION, bucket, key)
74 | logger .info("Signed URL: {}".format(signed_url))
75 |
76 | print ("bucket and key "+bucket+" "+key)
77 |
78 | # Launch MediaInfo
79 | # Pass the signed URL of the uploaded asset to MediaInfo as an input
80 | # MediaInfo will extract the technical metadata from the asset
81 | # The extracted metadata will be outputted in XML format and
82 | # stored in the variable xml_output
83 | xml_output = subprocess.check_output(["./mediainfo", "--full", "--output=XML", signed_url])
84 | print(xml_output)
85 |
86 | json_output = xmltodict.parse(xml_output)
87 |
88 | input['mediainfo'] = json_output['Mediainfo']
89 |
90 | #print(json.dumps(json_output, indent=4, cls=DecimalEncoder))
91 |
92 | # add expirtation timestamp for dynamo and save the event in dynamo
93 | job_input_info = event['detail']
94 | job_input_info["timestamp"] = tsevent
95 | job_input_info["timestampTTL"] = tsevent + MEDIAINFO_RETENTION_PERIOD
96 |
97 | s = json.dumps(job_input_info, cls=DecimalEncoder)
98 | job_input_info = json.loads(s, parse_float=decimal.Decimal)
99 | table = DYNAMO_CLIENT.Table(MEDIAINFOTABLE)
100 | response = table.put_item(Item = job_input_info)
101 | print(json.dumps(response, cls=DecimalEncoder))
102 |
103 | except Exception as e:
104 | print('An error occured {}'.format(e))
105 | traceback.print_stack()
106 | raise
107 | else:
108 | return True
109 |
--------------------------------------------------------------------------------
/9-MediainfoLambda/mediainfo.yaml:
--------------------------------------------------------------------------------
1 | Parameters:
2 |
3 | MediainfoTableTTL:
4 | Description: The amount of time in days that current job mediainfo data is cached in Dynamodb tables
5 | Type: Number
6 | Default: 1
7 |
8 |
9 | Mappings:
10 | SourceCode:
11 | General:
12 | S3Bucket: "rodeolabz"
13 | KeyPrefix: "vodtk/1c-mediaconvert-mediainfo"
14 |
15 | Resources:
16 |
17 | MediainfoTable:
18 | Type: AWS::DynamoDB::Table
19 | Properties:
20 | TableName: !Sub "${AWS::StackName}-MediainfoTable"
21 | AttributeDefinitions:
22 | -
23 | AttributeName: jobId
24 | AttributeType: S
25 | -
26 | AttributeName: timestamp
27 | AttributeType: N
28 | KeySchema:
29 | -
30 | AttributeName: jobId
31 | KeyType: HASH
32 | -
33 | AttributeName: timestamp
34 | KeyType: RANGE
35 | ProvisionedThroughput:
36 | ReadCapacityUnits: 5
37 | WriteCapacityUnits: 5
38 | TimeToLiveSpecification:
39 | AttributeName: timestampTTL
40 | Enabled: true
41 |
42 | MediainfoLambdaRole:
43 | Type: "AWS::IAM::Role"
44 | Properties:
45 | RoleName: !Sub "${AWS::StackName}-MediainfoLambdaRole"
46 | AssumeRolePolicyDocument:
47 | Version: 2012-10-17
48 | Statement:
49 | -
50 | Effect: Allow
51 | Principal:
52 | Service:
53 | - lambda.amazonaws.com
54 | Action:
55 | - sts:AssumeRole
56 | Policies:
57 | -
58 | PolicyName: !Sub "${AWS::StackName}-MediainfoLambdaPolicy"
59 | PolicyDocument:
60 | Statement:
61 | - Effect: Allow
62 | Action:
63 | - logs:CreateLogGroup
64 | - logs:CreateLogStream
65 | - logs:PutLogEvents
66 | Resource:
67 | - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/*"]]
68 | -
69 | Effect: Allow
70 | Action:
71 | - dynamodb:GetItem
72 | - dynamodb:PutItem
73 | - dynamodb:UpdateItem
74 | - dynamodb:Scan
75 | Resource:
76 | - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "MediainfoTable"]]
77 | -
78 | PolicyName: !Sub "${AWS::StackName}-EventLambdaMediainfoPolicy"
79 | PolicyDocument:
80 | Statement:
81 | - Effect: Allow
82 | Action:
83 | - s3:GetObjectVersionTorrent
84 | - s3:GetObjectAcl
85 | - s3:GetObject
86 | - s3:GetObjectTorrent
87 | - s3:GetObjectVersionTagging
88 | - s3:GetObjectVersionAcl
89 | - s3:GetObjectTagging
90 | - s3:GetObjectVersionForReplication
91 | - s3:GetObjectVersion
92 | - s3:ListMultipartUploadParts
93 | Resource: "arn:aws:s3:::*/*"
94 | - Effect: Allow
95 | Action:
96 | - s3:ListBucketByTags
97 | - s3:GetLifecycleConfiguration
98 | - s3:ListBucketMultipartUploads
99 | - s3:GetBucketTagging
100 | - s3:GetInventoryConfiguration
101 | - s3:GetBucketWebsite
102 | - s3:ListBucketVersions
103 | - s3:GetBucketLogging
104 | - s3:ListBucket
105 | - s3:GetAccelerateConfiguration
106 | - s3:GetBucketVersioning
107 | - s3:GetBucketAcl
108 | - s3:GetBucketNotification
109 | - s3:GetBucketPolicy
110 | - s3:GetReplicationConfiguration
111 | - s3:GetBucketRequestPayment
112 | - s3:GetBucketCORS
113 | - s3:GetAnalyticsConfiguration
114 | - s3:GetMetricsConfiguration
115 | - s3:GetBucketLocation
116 | - s3:GetIpConfiguration
117 | Resource: "arn:aws:s3:::*"
118 | - Effect: Allow
119 | Action:
120 | - s3:ListAllMyBuckets
121 | - s3:HeadBucket
122 | - s3:ListObjects
123 | Resource: "*"
124 |
125 | EventInvokeLambda:
126 | Type: AWS::Lambda::Permission
127 | Properties:
128 | FunctionName: !GetAtt MediainfoCollectorLambda.Arn
129 | Action: lambda:InvokeFunction
130 | Principal: events.amazonaws.com
131 | SourceArn:
132 | Fn::GetAtt:
133 | - "InputMediaConvertEventRule"
134 | - "Arn"
135 |
136 | InputMediaConvertEventRule:
137 | Type: "AWS::Events::Rule"
138 | Properties:
139 | Description: catches MediaConvert Events
140 | State: "ENABLED"
141 | EventPattern:
142 | source:
143 | - "aws.mediaconvert"
144 | detail-type:
145 | - "MediaConvert Job State Change"
146 | detail:
147 | status:
148 | - "INPUT_INFORMATION"
149 | Targets:
150 | -
151 | Arn:
152 | Fn::GetAtt:
153 | - "MediainfoCollectorLambda"
154 | - "Arn"
155 | Id: "TargetMediainfoCollectorLambda"
156 |
157 | MediainfoCollectorLambda:
158 | Type: AWS::Lambda::Function
159 | Properties:
160 | FunctionName: !Sub ${AWS::StackName}-EventCollector
161 | Description: Collect events, update status, make metrics
162 | Handler: mediainfo.lambda_handler
163 | Role: !GetAtt MediainfoLambdaRole.Arn
164 | Code:
165 | S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], !Ref "AWS::Region"]]
166 | S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "lambda.zip"]]
167 | Environment:
168 | Variables:
169 | MediainfoTable: !Ref MediainfoTable
170 | MediainfoTableTTL: !Ref MediainfoTableTTL
171 | Runtime: python3.6
172 | Timeout: 120
173 |
174 | APILambdaRole:
175 | Type: "AWS::IAM::Role"
176 | Properties:
177 | RoleName: !Sub "${AWS::StackName}-APILambdaRole"
178 | AssumeRolePolicyDocument:
179 | Version: 2012-10-17
180 | Statement:
181 | -
182 | Effect: Allow
183 | Principal:
184 | Service:
185 | - lambda.amazonaws.com
186 | Action:
187 | - sts:AssumeRole
188 | Policies:
189 | -
190 | PolicyName: !Sub "${AWS::StackName}-APILambdaPolicy"
191 | PolicyDocument:
192 | Statement:
193 | - Effect: Allow
194 | Action:
195 | - logs:CreateLogGroup
196 | - logs:CreateLogStream
197 | - logs:PutLogEvents
198 | Resource:
199 | - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/*"]]
200 | -
201 | Effect: Allow
202 | Action:
203 | - dynamodb:GetItem
204 | - dynamodb:Scan
205 | - dynamodb:BatchGetItem
206 | - dynamodb:DescribeTable
207 | - dynamodb:GetItem
208 | - dynamodb:ListTables
209 | - dynamodb:Query
210 | - dynamodb:Scan
211 | - dynamodb:DescribeReservedCapacity
212 | - dynamodb:DescribeReservedCapacityOfferings
213 | - dynamodb:ListTagsOfResource
214 | - dynamodb:DescribeTimeToLive
215 | - dynamodb:DescribeLimits
216 | - dynamodb:ListGlobalTables
217 | Resource:
218 | - "*"
219 | # - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "JobTable"]]
220 | # - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/",Ref: "MediaInfoTable"]]
221 |
222 | Outputs:
223 | MediainfoTable:
224 | Description: The name of the Table to get Mediainfo data from
225 | Value: !Ref MediainfoTable
226 | APILambdaRoleArn:
227 | Description: The ARN of the role to use to grant APIs access to Dyanmodb tables
228 | Value: !GetAtt APILambdaRole.Arn
229 |
--------------------------------------------------------------------------------
/9-MediainfoLambda/requirements.txt:
--------------------------------------------------------------------------------
1 | attrs==17.2.0
2 | awscli==1.14.53
3 | boto3==1.6.5
4 | botocore==1.9.6
5 | certifi==2023.7.22
6 | chalice==1.2.2
7 | chardet==3.0.4
8 | click==6.6
9 | colorama==0.3.7
10 | docutils==0.14
11 | elasticsearch==6.2.0
12 | enum34==1.1.6
13 | httpie==3.1.0
14 | idna==2.6
15 | jmespath==0.9.3
16 | lambda-uploader==1.2.0
17 | pyasn1==0.4.2
18 | Pygments==2.15.0
19 | python-dateutil==2.6.1
20 | PyYAML==5.4
21 | requests==2.31.0
22 | requests-aws4auth==0.9
23 | rsa==4.7
24 | s3transfer==0.1.13
25 | six==1.11.0
26 | timecode==1.2.0
27 | typing==3.5.3.0
28 | urllib3==1.26.18
29 | virtualenv==15.1.0
30 | xmltodict==0.11.0
31 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | AWS Media Services Simple Vod Workflow
2 | Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This project has been archived.
2 |
3 | # AWS Video On Demand with MediaConvert Workshop
4 |
5 | This workshop takes you through development of a video on demand (VOD)workflow using an encoder in the cloud to convert video content stored in Amazon S3 into new formats for storage, reuse and delivery. We will explore encoding video in different codecs and package formats, quality levels and output sizes for file based and streaming delivery. We will also look at how you can modify videos using clipping and stitching, add captions, detect Ads in input videos and burn in information to video to help with providing additional information about the video on playout.
6 |
7 | The workshop is broken up into multiple modules. Prerequisites for each module are listed at the start of the module README file. Some of the modules have AWS CloudFormation templates available that you can use to launch the necessary resources without manually creating them yourself if you'd like to skip ahead.
8 |
9 | # Workshop Requirements
10 |
11 | ### AWS Account
12 |
13 | In order to complete this workshop you'll need an AWS Account with access to create AWS MediaConvert, IAM, S3, and Lambda resources. The code and instructions in this workshop assume only one student is using a given AWS account at a time. If you try sharing an account with another student, you'll run into naming conflicts for certain resources. You can work around these by appending a unique suffix to the resources that fail to create due to conflicts, but the instructions do not provide details on the changes required to make this work.
14 |
15 | ### Billing
16 |
17 | MediaConvert jobs will incur charges based on the region you are using for the workshop at the rates described in the MediaConvert pricing page: https://aws.amazon.com/mediaconvert/pricing/ .
18 |
19 | **Each MediaConvert job from this lab produces outputs with the following characteristics:**
20 |
21 | ABR stack
22 | * 3 outputs: 1280x720, 960x540, 680x360
23 |
24 | MP4
25 | * 1 output: 1280x720
26 |
27 | Thumbnails
28 | * 1 output: 1280x720
29 |
30 | All ouputs:
31 | * MPEG-2 Codec
32 | * 30 - 60 FPS
33 | * 1.5 - 2 minutes long depending on which job in the lab you are running.
34 |
35 | **Other lab resources**
36 |
37 | S3 and other resources you will launch as part of this workshop are eligible for the AWS free tier if your account is less than 12 months old. See the [AWS Free Tier page](https://aws.amazon.com/free/) for more details.
38 |
39 | ### Browser
40 |
41 | We recommend you use the latest version of Chrome to complete this workshop.
42 |
43 | ### Video player
44 |
45 | Videos can be played out in the browser if the browser supports them. We will also use:
46 | * **JW Player Stream Tester** https://developer.jwplayer.com/tools/stream-tester/
47 |
48 | ### Text Editor
49 |
50 | You will need a local text editor for making minor updates to configuration files.
51 |
52 | ### Download the Workshop
53 |
54 | You will need to download this project to your computer in order to create the browser page, run CloudFormation templates, and to work with caption files.
55 |
56 | # Workshop Modules
57 |
58 | - [**AWS IAM and S3**](1-IAMandS3/README.md) - This module guides the participant in configuring permissions for the AWS services used in this workshop. You will learn how to create a policy that permits users access to AWS Elemental MediaConvert in your account. You will also create a role to pass to MediaConvert so it can access resources in your account it needs to run jobs.
59 |
60 | - [**AWS Elemental MediaConvert Jobs**](2-MediaConvertJobs/README.md) - This module guides the participant in creating an AWS Elemental MediaConvert job to convert an HLS input into HLS, MP4 and Thumbnail outputs. You will learn about file based and adaptive bitrate delivery and the basics of producing video output for different purposes.
61 |
62 | - [**Modifying AWS Elemental MediaConvert Inputs**](3-Inputs/README.md) - This module guides the participant in clipping and stitching inputs to AWS MediaConvert. You will explore other types on Input modications are available.
63 |
64 | - [**Modifying AWS Elemental MediaConvert Outputs**](4-Outputs/README.md) - This module guides the participant in different kinds of information that they can "burn-in" to a video ouput. They will create a video with timecodes and watermarks burned in to the elemental video stream of one of the outputs of their job.
65 |
66 | - [**Working with Captions**](5-Captions/README.md) - This module guides the participant in creating media assets with burned in caption generated from a side-car captions file.
67 |
68 |
69 | - [**Automating Jobs with Lambda and S3 Event Triggers**](7-MediaConvertJobLambda/README.md) - This module guides the participant in creating an automated "watchfolder" workflow. Video files added to an S3 bucket automatically trigger a MediaConvert job to run on the uploaded files.
70 |
71 | - [**VOD Ad Insertion with AWS Elemental MediaTailor**](11-VODMediaTailor) - This module guides the participant in inserting ads to their HLS media asset using the AWS Elemental MediaTailor service.
72 |
73 | # Start the Workshop
74 |
75 | Move forward to the first module for [**AWS IAM and S3**](1-IAMandS3/README.md).
76 |
77 | # Credits
78 |
79 | _Sea Waves Sound_ in TRAILER.mp4 recorded by Mike Koenig. Licensed under the Creative Commons Attribution 3.0 license.
80 |
81 | Captions text in CAPTIONS_en.srt and CAPTIONS_ru.srt © copyright Blender Foundation | www.sintel.org. Licenced under the Creative Commons Attribution 3.0 license.
82 |
83 | # Contributors
84 |
85 | Alex Burkleaux
86 |
87 | Eddie Goynes
88 |
89 | Jeremy Johnson
90 |
--------------------------------------------------------------------------------
/VODConsoleWorkshop.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates a static website and backend AWS services for the AWS Elemental Media Services VOD workshop
6 |
7 | Resources:
8 |
9 | MediaConvertIAMandS3:
10 | Type: "AWS::CloudFormation::Stack"
11 | Properties:
12 | #Parameters:
13 | # MediaBucketName: !Sub "${AWS::StackName}"
14 | TemplateURL: https://s3.amazonaws.com/rodeolabz-us-west-2/vodconsole/cf/MediaConvertIAMandS3.yaml
15 | TimeoutInMinutes: 60
16 | WatchFolder:
17 | Type: "AWS::CloudFormation::Stack"
18 | Properties:
19 | Parameters:
20 | Parameters:
21 | MediaBucket: !GetAtt MediaConvertIAMandS3.Outputs.MediaBucket
22 | MediaConvertRoleArn: !GetAtt MediaConvertIAMandS3.Outputs.MediaConvertRoleArn
23 | TemplateURL: https://s3.amazonaws.com/rodeolabz-us-west-2/vodconsole/cf/WatchFolder.yaml
24 | TimeoutInMinutes: 60
25 |
26 | Outputs:
27 | MediaBucketName:
28 | Value: !GetAtt MediaConvertIAMandS3.Outputs.MediaBucket
29 | MediaConvertRole:
30 | Value: !GetAtt MediaConvertIAMandS3.Outputs.MediaConvertRole
31 | MediaConvertRoleArn:
32 | Value: !GetAtt MediaConvertIAMandS3.Outputs.MediaConvertRoleArn
33 | WatchFolderBucketName:
34 | Value: !GetAtt WatchFolder.Outputs.WatchFolderBucket
35 | LambdaRoleArn:
36 | Value: !GetAtt WatchFolder.Outputs.LambdaRoleArn
37 |
--------------------------------------------------------------------------------
/Workflow-WatchfolderAndNotification/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 | */*.zip
3 | ziplambda.sh
4 |
--------------------------------------------------------------------------------
/Workflow-WatchfolderAndNotification/README.md:
--------------------------------------------------------------------------------
1 | # Serverless Video Conversion Watchfolder Workflow
2 | ## Automatically trigger MediaConvert jobs on S3 objects and get notification when the jobs complete
3 |
4 | Video delivery workflows use MediaConvert to transcode source videos into formats that ensure high quality playback on all of the devices and platforms needed to reach target viewers. A watchfolder is a common method for automating video ingest and delivery workflows. Users with video ready for delivery upload their files to a known storage location. The upload automatically triggers an ingest workflow that converts the video to formats suitable for delivery and stores them in the cloud for on-demand viewing. The Serverless Video Conversion Watchfolder Workflow solves this problem by using [Amazon S3](https://aws.amazon.com/s3), [AWS Lambda](https://aws.amazon.com/lambda/), [AWS MediaConvert](https://aws.amazon.com/mediaconvert/), [Amazon CloudWatch Events](https://aws.amazon.com/cloudwatch) and [Amazon Simple Notification Service](https://aws.amazon.com/sns).
5 |
6 | The workflow will create the following outputs for each video uploaded to the WatchFolder S3 bucket /inputs folder:
7 | - An Apple HLS adaptive bitrate stream for playout on multiple sized devices and varying bandwiths.
8 | - An MP4 stream
9 | - Thumbnail images collected at intervals
10 |
11 | This repository contains sample code for the Lambda function depicted in the diagram below as well as an [AWS CloudFormation](https://aws.amazon.com/cloudformation/) template for creating the function and related resources.
12 |
13 | There is a companion tutorial in [README-tutorial.md](./README-tutorial.md) for creating this workflow using the AWS console. Use the tutorial to go in depth on how the workflow is built and configured.
14 |
15 | To see some of the other powerful broadcast grade encoding features of AWS Elemental MediaConvert check out the getting started page [here](https://aws.amazon.com/mediaconvert/)
16 |
17 | 
18 |
19 | ## Adapting this workflow to use different encoding settings
20 |
21 | It's easy to create outputs with other formats supported by MediaConvert. MediaConvert job settings can be placed in JSON files in the WatchFolder S3 bucket /jobs folder. The workflow will run a job for each settings file. If no setting are specified, then the Default job included in this project will run.
22 |
23 | ## Walkthrough of the Workflow
24 | 1. The Ingest user uploads a video to the WatchFolder bucket /inputs folder in S3. Only files added to the /inputs folder will trigger the workflow.
25 |
26 | 2. The s3:PutItem event triggers a Lambda function that calls MediaConvert to convert the videos.
27 |
28 | 3. Converted videos are stored in S3 by MediaConvert.
29 |
30 | 4. When the conversion job finishes MediaConvert emits aws:mediaconvert _Job State Change Event_ type CloudWatch events with the job status.
31 |
32 | 5. The COMPLETE and ERROR status events trigger SNS to send notifications to subscribers.
33 |
34 | ## Running the Example
35 |
36 | ### Launching the Stack on AWS
37 |
38 | The backend infrastructure can be deployed in US West - Oregon (us-west-2) using the provided CloudFormation template.
39 | Click **Launch Stack** to launch the template in the US West - Oregon (us-west-2) region in your account:
40 |
41 | A CloudFormation template is provided for this module in the file `WatchFolder.yaml` to build the workflow automatically. Click **Launch Stack** to launch the template in your account in the region of your choice :
42 |
43 | Region| Launch
44 | ------|-----
45 | US East (N. Virginia) | [](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=emc-watchfolder&templateURL=https://s3.amazonaws.com/rodeolabz-us-east-1/vodtk/1a-mediaconvert-watchfolder/WatchFolder.yaml)
46 | US West (Oregon) | [](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=emc-watchfolder&templateURL=https://s3.amazonaws.com/rodeolabz-us-west-2/vodtk/1a-mediaconvert-watchfolder/WatchFolder.yaml)
47 |
48 | 1. Make sure to enter an e-mail address to use for workflow notifications in the workflow input form.
49 |
50 | (On the last page of the wizard, make sure to:
51 |
52 | 2. Click the checkboxes to give AWS CloudFormation permission to **"create IAM resources"** and **"create IAM resources with custom names"**
53 | 1. Click **"Execute"**
54 | )
55 |
56 | The information about the resources created is in the **Outputs** tab of the stack. Save this in a browser tab so you can use it later when you create other stacks and MediaConvert jobs.
57 |
58 | 
59 |
60 | ## Testing the Example
61 |
62 | You can use your own video or use the test.mp4 video included in this folder to test the workflow.
63 |
64 | 1. Open the S3 console overview page for the watchfolder S3 bucket you created earlier.
65 | 2. Click on **+ Create folder** and enter `inputs` as the folder name and click **Save**
66 | 3. Click on the **inputs** folder link to open it.
67 | 1. Select **Upload** and then choose the file `test.mp4` from the directory for this lab module on your computer.
68 | 1. Note the time that the upload completed.
69 | 1. Open the MediaConvert jobs page and find a job for the input 'test.mp4' that was started near the time your upload completed.
70 |
71 | 
72 |
73 | 1. Click on the jobId link to open the job details page.
74 | 2. Click on the link for the MP4 or HLS output (depending on what is supported by your browser). This will take you to the S3 folder where your output is located.
75 | 3. Click on the ouput object link.
76 | 4. Play the test video by clicking on the S3 object http resource listed under the **Link** label.
77 |
78 | 
79 |
80 | 5. Check the email you used to setup the workflow. You should have a message similar to the one below. The link should take you to the same job details page you found manually in the previous steps:
81 |
82 | 
83 |
84 | ## Cleaning Up the Stack Resources
85 |
86 | To remove all resources created by this example, do the following:
87 |
88 | 1. Delete the video files in the WatchFolder and MediaBucket S3 buckets.
89 | 2. Delete the CloudFormation stack.
90 | 1. Delete the CloudWatch log groups associated with each Lambda function created by the CloudFormation stack.
91 |
92 | ## CloudFormation Template Resources
93 | The following sections explain all of the resources created by the CloudFormation template provided with this example.
94 |
95 | ### CloudWatch Events
96 |
97 | - **NotifyEventRule** - Notification rule that matches MediaConvert COMPLETE and ERROR events created from this workflow. The rule uses userMetadata.application JSON values that are set when a MediaConvert job is created to identify jobs from this workflow. We are using the name of the stack as the value of the userMetadata.application (see code in [convert.py](./convert.py)). The target of the rule is the NotificationSns SNS Topic. An InputTransformer is applied by the rule to format the information in the event before it passes on the messages to the target.
98 |
99 | ### IAM
100 | - **MediaConvertRole** - A role that is passed to MediaConvert Python SDK via the API. MediaConvert used this role to obtain access the the account resources needed to complete a conversion job.
101 |
102 | - **LambdaRole** - an IAM role with policies to allow LambdaConvert to invoke the MediaConvert service, pass the MediaConvert role and access the WatchFolder bucket to look for user defined job inputs.
103 |
104 | ### Lambda
105 | - **S3InvokeLambda** - a Lambda Permission that gives S3 permission to invoke the MediaConvert lambda.
106 |
107 | - **LambdaConvert** - a Lambda function that gets a video from the s3:putItem event and runs MediaConverts jobs from JSON settings in the s3://Watchfolder/jobs folder.
108 |
109 | ### S3
110 |
111 | - **WatchFolder** - S3 bucket used to store inputs and optional job settings to the conversion workflow. The NotificationConfiguration sets up the lambda trigger whenever an object is uploaded to the **/inputs** folder. MediaCOnvert job settings JSON files can be place in the **/jobs** folder. The workflow will run a job for each settings file it finds there. The bucket is set to expire objects that are more than 7 days old. This setting is useful for testing, but can be removed, if needed.
112 |
113 | - **MediaBucket** - S3 bucket used to store outputs of the conversion workflow. Cross Origin Resource sharing is enabled to allow the videos to be played out from websites and browsers. The bucket is set to expire objects that are more than 7 days old. This setting is useful for testing, but can be removed, if needed.
114 |
115 | - **MediaBucketPolicy** - Sets policy for MediaBucket to public read for testing.
116 |
117 | ### SNS
118 |
119 | - **NotificationTopicPolicy** - an SNS Topic Policy that give permission to CloudWatch events service to invoke SNS notifications in this account.
120 |
121 | - **NotificationSns** - an SNS Topic for publishing notifications about the status of finished jobs from our workflow.
122 |
123 | ## Additional resources
124 |
125 | [AWS Video On Demand with MediaConvert Workshop](https://github.com/aws-samples/aws-media-services-simple-vod-workflow/blob/master/README.md) - use the MediaConvert console to develop video conversion jobs for adaptive bitrate and file-based delivery. Includes adding captions, clipping and stitching and watermarking.
126 |
127 | [Video on Demand on AWS](https://aws.amazon.com/answers/media-entertainment/video-on-demand-on-aws/) - a well-architected reference architecture that you can use to get started on production workflows. Video on Demand on AWS has a similar workflow to this sample, but adds Step Functions to orchestrate the workflow, a Cloudfront distribution to deliver efficiently at the edge, and a backend data store to keep track of the status and history of the ingested videos.
128 |
129 | ## License
130 |
131 | This sample is licensed under Apache 2.0.
--------------------------------------------------------------------------------
/Workflow-WatchfolderAndNotification/WatchFolder.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: "2010-09-09"
3 |
4 | Description:
5 | Creates an S3 watchfolder and automated video conversion workflow
6 |
7 | Parameters:
8 | NotifcationEmail:
9 | Description: Email address for SNS notifications
10 | Type: String
11 | Default: elementalrodeo@gmail.com
12 | AllowedPattern: "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$"
13 |
14 | Mappings:
15 | SourceCode:
16 | General:
17 | S3Bucket: "rodeolabz"
18 | KeyPrefix: "vodtk/1a-mediaconvert-watchfolder"
19 |
20 | Resources:
21 |
22 | MediaConvertRole:
23 | Type: AWS::IAM::Role
24 | Properties:
25 | RoleName: !Sub "${AWS::StackName}-MediaConvertRole"
26 | AssumeRolePolicyDocument:
27 | Version: 2012-10-17
28 | Statement:
29 | -
30 | Effect: Allow
31 | Principal:
32 | Service:
33 | - "mediaconvert.amazonaws.com"
34 | - "mediaconvert.us-east-1.amazonaws.com"
35 | - "mediaconvert.ap-northeast-1.amazonaws.com"
36 | - "mediaconvert.ap-southeast-1.amazonaws.com"
37 | - "mediaconvert.ap-southeast-2.amazonaws.com"
38 | - "mediaconvert.eu-central-1.amazonaws.com"
39 | - "mediaconvert.eu-west-1.amazonaws.com"
40 | - "mediaconvert.us-east-1.amazonaws.com"
41 | - "mediaconvert.us-west-1.amazonaws.com"
42 | - "mediaconvert.us-west-2.amazonaws.com"
43 | Action:
44 | - sts:AssumeRole
45 | Policies:
46 | -
47 | PolicyName: !Sub "${AWS::StackName}-MediaConvertPolicy"
48 | PolicyDocument:
49 | Statement:
50 | -
51 | Effect: "Allow"
52 | Action:
53 | - "s3:*"
54 | Resource:
55 | - "*"
56 | -
57 | Effect: "Allow"
58 | Action:
59 | - "autoscaling:Describe*"
60 | - "cloudwatch:*"
61 | - "logs:*"
62 | - "sns:*"
63 | Resource:
64 | - "*"
65 |
66 | LambdaRole:
67 | Type: AWS::IAM::Role
68 | Properties:
69 | RoleName: !Sub "${AWS::StackName}-LambdaRole"
70 | AssumeRolePolicyDocument:
71 | Version: 2012-10-17
72 | Statement:
73 | -
74 | Effect: Allow
75 | Principal:
76 | Service:
77 | - lambda.amazonaws.com
78 | Action:
79 | - sts:AssumeRole
80 | Policies:
81 | -
82 | PolicyName: !Sub "${AWS::StackName}-LambdaPolicy"
83 | PolicyDocument:
84 | Version: 2012-10-17
85 | Statement:
86 | -
87 | Sid: Logging
88 | Effect: Allow
89 | Action:
90 | - "logs:CreateLogGroup"
91 | - "logs:CreateLogStream"
92 | - "logs:PutLogEvents"
93 | Resource: "*"
94 | -
95 | Sid: PassRole
96 | Effect: Allow
97 | Action:
98 | - iam:PassRole
99 | Resource:
100 | - !GetAtt MediaConvertRole.Arn
101 | - Sid: MediaConvertService
102 | Effect: Allow
103 | Action:
104 | - "mediaconvert:*"
105 | Resource:
106 | - "*"
107 | -
108 | Sid: InputBucket
109 | Effect: Allow
110 | Action:
111 | - "s3:*"
112 | Resource: "*"
113 |
114 | S3InvokeLambda:
115 | Type: AWS::Lambda::Permission
116 | Properties:
117 | FunctionName: !GetAtt LambdaConvert.Arn
118 | Action: lambda:InvokeFunction
119 | Principal: s3.amazonaws.com
120 | SourceAccount: !Ref AWS::AccountId
121 |
122 | WatchFolder:
123 | Type: AWS::S3::Bucket
124 | Properties:
125 | LifecycleConfiguration:
126 | Rules:
127 | - Id: ExpireRule
128 | Status: Enabled
129 | ExpirationInDays: '7'
130 | NotificationConfiguration:
131 | LambdaConfigurations:
132 | -
133 | Function: !GetAtt LambdaConvert.Arn
134 | Event: "s3:ObjectCreated:*"
135 | Filter:
136 | S3Key:
137 | Rules:
138 | -
139 | Name: prefix
140 | Value: inputs/
141 | DeletionPolicy: Retain
142 |
143 | LambdaConvert:
144 | Type: AWS::Lambda::Function
145 | Properties:
146 | FunctionName: !Sub ${AWS::StackName}-convert
147 | Description: Converts input video
148 | Handler: convert.handler
149 | Role: !GetAtt LambdaRole.Arn
150 | Code:
151 | S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], !Ref "AWS::Region"]]
152 | S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "lambda.zip"]]
153 | Environment:
154 | Variables:
155 | DestinationBucket: !Ref MediaBucket
156 | MediaConvertRole: !GetAtt MediaConvertRole.Arn
157 | Application: !Sub "${AWS::StackName}"
158 | Runtime: python3.7
159 | Timeout: 120
160 |
161 | MediaBucket:
162 | Properties:
163 | WebsiteConfiguration:
164 | IndexDocument: index.html
165 | CorsConfiguration:
166 | CorsRules:
167 | - AllowedHeaders: ['*']
168 | AllowedMethods: [GET]
169 | AllowedOrigins: ['*']
170 | ExposedHeaders: [Date]
171 | Id: myCORSRuleId1
172 | MaxAge: '3600'
173 | LifecycleConfiguration:
174 | Rules:
175 | - Id: ExpireRule
176 | Status: Enabled
177 | ExpirationInDays: '7'
178 | DeletionPolicy: Retain
179 | Type: "AWS::S3::Bucket"
180 |
181 | MediaBucketPolicy:
182 | Properties:
183 | Bucket: !Ref MediaBucket
184 | PolicyDocument:
185 | Version: 2012-10-17
186 | Statement:
187 | -
188 | Effect: Allow
189 | Principal: "*"
190 | Action: s3:GetObject
191 | Resource: !Sub "arn:aws:s3:::${MediaBucket}/*"
192 | Type: "AWS::S3::BucketPolicy"
193 |
194 | NotificationTopicPolicy:
195 | Type: AWS::SNS::TopicPolicy
196 | Properties:
197 | PolicyDocument:
198 | Version: '2012-10-17'
199 | Statement:
200 | - Sid: AllowCloudWatchEvents
201 | Effect: Allow
202 | Principal:
203 | Service: events.amazonaws.com
204 | Action: sns:Publish
205 | Resource: "*" #grant rights to all SNS topics (narrow it down if security is an issue)
206 | Topics:
207 | - !Ref NotificationSns
208 |
209 | NotificationSns:
210 | Type: AWS::SNS::Topic
211 | Properties:
212 | Subscription:
213 | -
214 | Endpoint: !Ref NotifcationEmail
215 | Protocol: email
216 |
217 | NotifyEventRule:
218 | Type: "AWS::Events::Rule"
219 | Properties:
220 | Description: catches MediaConvert Complete and Error events and sends to SNS topic
221 | State: "ENABLED"
222 | EventPattern:
223 | source:
224 | - "aws.mediaconvert"
225 | detail:
226 | status:
227 | - "COMPLETE"
228 | - "ERROR"
229 | userMetadata:
230 | application:
231 | - !Sub "${AWS::StackName}"
232 | Targets:
233 | -
234 | Arn: !Ref NotificationSns
235 | Id: "TargetSNS1"
236 | InputTransformer:
237 | InputPathsMap:
238 | jobId: "$.detail.jobId"
239 | region: "$.region"
240 | status: "$.detail.status"
241 | InputTemplate: '"Job finished with status . Job details: https://.console.aws.amazon.com/mediaconvert/home?region=#/jobs/summary/"'
242 |
243 | Outputs:
244 | MediaBucket:
245 | Value: !Ref MediaBucket
246 | MediaConvertRoleArn:
247 | Value: !GetAtt MediaConvertRole.Arn
248 | WatchFolderBucket:
249 | Value: !Ref WatchFolder
250 | LambdaRoleArn:
251 | Value: !GetAtt LambdaRole.Arn
252 |
--------------------------------------------------------------------------------
/Workflow-WatchfolderAndNotification/convert.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import glob
4 | import json
5 | import os
6 | import uuid
7 | import boto3
8 | import datetime
9 | import random
10 | from urllib.parse import urlparse
11 | import logging
12 |
13 | from botocore.client import ClientError
14 |
15 | logger = logging.getLogger()
16 | logger.setLevel(logging.INFO)
17 |
18 | S3 = boto3.resource('s3')
19 |
20 | def handler(event, context):
21 | '''
22 | Watchfolder handler - this lambda is triggered when video objects are uploaded to the
23 | SourceS3Bucket/inputs folder.
24 |
25 | It will look for two sets of file inputs:
26 | SourceS3Bucket/inputs/SourceS3Key:
27 | the input video to be converted
28 |
29 | SourceS3Bucket/jobs/*.json:
30 | job settings for MediaConvert jobs to be run against the input video. If
31 | there are no settings files in the jobs folder, then the Default job will be run
32 | from the job.json file in lambda environment.
33 |
34 | Ouput paths stored in outputGroup['OutputGroupSettings']['DashIsoGroupSettings']['Destination']
35 | are constructed from the name of the job settings files as follows:
36 |
37 | s3://///
38 |
39 | '''
40 |
41 | assetID = str(uuid.uuid4())
42 | sourceS3Bucket = event['Records'][0]['s3']['bucket']['name']
43 | sourceS3Key = event['Records'][0]['s3']['object']['key']
44 | sourceS3 = 's3://'+ sourceS3Bucket + '/' + sourceS3Key
45 | destinationS3 = 's3://' + os.environ['DestinationBucket']
46 | mediaConvertRole = os.environ['MediaConvertRole']
47 | application = os.environ['Application']
48 | region = os.environ['AWS_DEFAULT_REGION']
49 | statusCode = 200
50 | jobs = []
51 | job = {}
52 |
53 | # Use MediaConvert SDK UserMetadata to tag jobs with the assetID
54 | # Events from MediaConvert will have the assetID in UserMedata
55 | jobMetadata = {}
56 | jobMetadata['assetID'] = assetID
57 | jobMetadata['application'] = application
58 | jobMetadata['input'] = sourceS3
59 |
60 | try:
61 |
62 | # Build a list of jobs to run against the input. Use the settings files in WatchFolder/jobs
63 | # if any exist. Otherwise, use the default job.
64 |
65 | jobInput = {}
66 | # Iterates through all the objects in jobs folder of the WatchFolder bucket, doing the pagination for you. Each obj
67 | # contains a jobSettings JSON
68 | bucket = S3.Bucket(sourceS3Bucket)
69 | for obj in bucket.objects.filter(Prefix='jobs/'):
70 | if obj.key != "jobs/":
71 | jobInput['filename'] = obj.key
72 | logger.info('jobInput: %s', jobInput['filename'])
73 |
74 | jobInput['settings'] = json.loads(obj.get()['Body'].read())
75 | logger.info(json.dumps(jobInput['settings']))
76 |
77 | jobs.append(jobInput)
78 |
79 | # Use Default job settings in the lambda zip file in the current working directory
80 | if not jobs:
81 |
82 | with open('job.json') as json_data:
83 | jobInput['filename'] = 'Default'
84 | logger.info('jobInput: %s', jobInput['filename'])
85 |
86 | jobInput['settings'] = json.load(json_data)
87 | logger.info(json.dumps(jobInput['settings']))
88 |
89 | jobs.append(jobInput)
90 |
91 | # get the account-specific mediaconvert endpoint for this region
92 | mediaconvert_client = boto3.client('mediaconvert', region_name=region)
93 | endpoints = mediaconvert_client.describe_endpoints()
94 |
95 | # add the account-specific endpoint to the client session
96 | client = boto3.client('mediaconvert', region_name=region, endpoint_url=endpoints['Endpoints'][0]['Url'], verify=False)
97 |
98 | for j in jobs:
99 | jobSettings = j['settings']
100 | jobFilename = j['filename']
101 |
102 | # Save the name of the settings file in the job userMetadata
103 | jobMetadata['settings'] = jobFilename
104 |
105 | # Update the job settings with the source video from the S3 event
106 | jobSettings['Inputs'][0]['FileInput'] = sourceS3
107 |
108 | # Update the job settings with the destination paths for converted videos. We want to replace the
109 | # destination bucket of the output paths in the job settings, but keep the rest of the
110 | # path
111 | destinationS3 = 's3://' + os.environ['DestinationBucket'] + '/' \
112 | + os.path.splitext(os.path.basename(sourceS3Key))[0] + '/' \
113 | + os.path.splitext(os.path.basename(jobFilename))[0]
114 |
115 | for outputGroup in jobSettings['OutputGroups']:
116 |
117 | logger.info("outputGroup['OutputGroupSettings']['Type'] == %s", outputGroup['OutputGroupSettings']['Type'])
118 |
119 | if outputGroup['OutputGroupSettings']['Type'] == 'FILE_GROUP_SETTINGS':
120 | templateDestination = outputGroup['OutputGroupSettings']['FileGroupSettings']['Destination']
121 | templateDestinationKey = urlparse(templateDestination).path
122 | logger.info("templateDestinationKey == %s", templateDestinationKey)
123 | outputGroup['OutputGroupSettings']['FileGroupSettings']['Destination'] = destinationS3+templateDestinationKey
124 |
125 | elif outputGroup['OutputGroupSettings']['Type'] == 'HLS_GROUP_SETTINGS':
126 | templateDestination = outputGroup['OutputGroupSettings']['HlsGroupSettings']['Destination']
127 | templateDestinationKey = urlparse(templateDestination).path
128 | logger.info("templateDestinationKey == %s", templateDestinationKey)
129 | outputGroup['OutputGroupSettings']['HlsGroupSettings']['Destination'] = destinationS3+templateDestinationKey
130 |
131 | elif outputGroup['OutputGroupSettings']['Type'] == 'DASH_ISO_GROUP_SETTINGS':
132 | templateDestination = outputGroup['OutputGroupSettings']['DashIsoGroupSettings']['Destination']
133 | templateDestinationKey = urlparse(templateDestination).path
134 | logger.info("templateDestinationKey == %s", templateDestinationKey)
135 | outputGroup['OutputGroupSettings']['DashIsoGroupSettings']['Destination'] = destinationS3+templateDestinationKey
136 |
137 | elif outputGroup['OutputGroupSettings']['Type'] == 'DASH_ISO_GROUP_SETTINGS':
138 | templateDestination = outputGroup['OutputGroupSettings']['DashIsoGroupSettings']['Destination']
139 | templateDestinationKey = urlparse(templateDestination).path
140 | logger.info("templateDestinationKey == %s", templateDestinationKey)
141 | outputGroup['OutputGroupSettings']['DashIsoGroupSettings']['Destination'] = destinationS3+templateDestinationKey
142 |
143 | elif outputGroup['OutputGroupSettings']['Type'] == 'MS_SMOOTH_GROUP_SETTINGS':
144 | templateDestination = outputGroup['OutputGroupSettings']['MsSmoothGroupSettings']['Destination']
145 | templateDestinationKey = urlparse(templateDestination).path
146 | logger.info("templateDestinationKey == %s", templateDestinationKey)
147 | outputGroup['OutputGroupSettings']['MsSmoothGroupSettings']['Destination'] = destinationS3+templateDestinationKey
148 | else:
149 | logger.error("Exception: Unknown Output Group Type %s", outputGroup['OutputGroupSettings']['Type'])
150 | statusCode = 500
151 |
152 | logger.info(json.dumps(jobSettings))
153 |
154 | # Convert the video using AWS Elemental MediaConvert
155 | job = client.create_job(Role=mediaConvertRole, UserMetadata=jobMetadata, Settings=jobSettings)
156 |
157 | except Exception as e:
158 | logger.error('Exception: %s', e)
159 | statusCode = 500
160 | raise
161 |
162 | finally:
163 | return {
164 | 'statusCode': statusCode,
165 | 'body': json.dumps(job, indent=4, sort_keys=True, default=str),
166 | 'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'}
167 | }
168 |
--------------------------------------------------------------------------------
/Workflow-WatchfolderAndNotification/test.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/Workflow-WatchfolderAndNotification/test.mp4
--------------------------------------------------------------------------------
/images/HLS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/HLS.png
--------------------------------------------------------------------------------
/images/Kibana.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/Kibana.png
--------------------------------------------------------------------------------
/images/MediaConvertBasePipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/MediaConvertBasePipeline.png
--------------------------------------------------------------------------------
/images/ProgressMetricsStack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/ProgressMetricsStack.png
--------------------------------------------------------------------------------
/images/ProgressStack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/ProgressStack.png
--------------------------------------------------------------------------------
/images/S3-access-lambda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/S3-access-lambda.png
--------------------------------------------------------------------------------
/images/WatchfolderSlide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/WatchfolderSlide.png
--------------------------------------------------------------------------------
/images/WorkloadMonitoringStack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/WorkloadMonitoringStack.png
--------------------------------------------------------------------------------
/images/beaches-channel-captions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/beaches-channel-captions.png
--------------------------------------------------------------------------------
/images/burnin-captions-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/burnin-captions-settings.png
--------------------------------------------------------------------------------
/images/cf-outputs-role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cf-outputs-role.png
--------------------------------------------------------------------------------
/images/cf-simple-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cf-simple-events.png
--------------------------------------------------------------------------------
/images/cf-watchfolder-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cf-watchfolder-stack.png
--------------------------------------------------------------------------------
/images/cloudformation-outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cloudformation-outputs.png
--------------------------------------------------------------------------------
/images/cloudwatch-rule-allevents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cloudwatch-rule-allevents.png
--------------------------------------------------------------------------------
/images/configure-test-event.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/configure-test-event.png
--------------------------------------------------------------------------------
/images/consoleUserMetadata.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/consoleUserMetadata.png
--------------------------------------------------------------------------------
/images/create-lambda-convert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/create-lambda-convert.png
--------------------------------------------------------------------------------
/images/create-source-bucket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/create-source-bucket.png
--------------------------------------------------------------------------------
/images/custom-vocabulary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/custom-vocabulary.png
--------------------------------------------------------------------------------
/images/cw-event-rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cw-event-rule.png
--------------------------------------------------------------------------------
/images/cw-input-rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/cw-input-rule.png
--------------------------------------------------------------------------------
/images/dynamoTTL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/dynamoTTL.png
--------------------------------------------------------------------------------
/images/dynamotable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/dynamotable.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-basic-info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-basic-info.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-function-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-function-code.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-job-inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-job-inputs.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-job.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-s3-trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-s3-trigger.png
--------------------------------------------------------------------------------
/images/edl-mediaconvert-trigger-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/edl-mediaconvert-trigger-config.png
--------------------------------------------------------------------------------
/images/emc-explained-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emc-explained-output.png
--------------------------------------------------------------------------------
/images/emp-vod-asset-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-asset-details.png
--------------------------------------------------------------------------------
/images/emp-vod-asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-asset.png
--------------------------------------------------------------------------------
/images/emp-vod-cw-event-rule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-cw-event-rule.png
--------------------------------------------------------------------------------
/images/emp-vod-ingest-asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-ingest-asset.png
--------------------------------------------------------------------------------
/images/emp-vod-lambda-env-vars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-lambda-env-vars.png
--------------------------------------------------------------------------------
/images/emp-vod-packaging-group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-packaging-group.png
--------------------------------------------------------------------------------
/images/emp-vod-playback-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-playback-details.png
--------------------------------------------------------------------------------
/images/emp-vod-playback-preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-playback-preview.png
--------------------------------------------------------------------------------
/images/emp-vod-role-lambda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/emp-vod-role-lambda.png
--------------------------------------------------------------------------------
/images/esam-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/esam-settings.png
--------------------------------------------------------------------------------
/images/eventlambdarole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/eventlambdarole.png
--------------------------------------------------------------------------------
/images/hlsjs-metrics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/hlsjs-metrics.png
--------------------------------------------------------------------------------
/images/hlsjs-quality-levels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/hlsjs-quality-levels.png
--------------------------------------------------------------------------------
/images/hlsjs-vanlife-hls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/hlsjs-vanlife-hls.png
--------------------------------------------------------------------------------
/images/iam-role-mediaconvert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/iam-role-mediaconvert.png
--------------------------------------------------------------------------------
/images/input-captions-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/input-captions-settings.png
--------------------------------------------------------------------------------
/images/kibana-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/kibana-dashboard.png
--------------------------------------------------------------------------------
/images/kibana-saved-objects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/kibana-saved-objects.png
--------------------------------------------------------------------------------
/images/kibana-timepicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/kibana-timepicker.png
--------------------------------------------------------------------------------
/images/lambda-convert-author.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-convert-author.png
--------------------------------------------------------------------------------
/images/lambda-environment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-environment.png
--------------------------------------------------------------------------------
/images/lambda-function-code-wfnotification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-function-code-wfnotification.png
--------------------------------------------------------------------------------
/images/lambda-function-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-function-code.png
--------------------------------------------------------------------------------
/images/lambda-mediainfo-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-mediainfo-code.png
--------------------------------------------------------------------------------
/images/lambda-mediainfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-mediainfo.png
--------------------------------------------------------------------------------
/images/lambda-s3-trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-s3-trigger.png
--------------------------------------------------------------------------------
/images/lambda-simeple-event-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-simeple-event-test.png
--------------------------------------------------------------------------------
/images/lambda-simple-event-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-simple-event-code.png
--------------------------------------------------------------------------------
/images/lambda-simple-event-collector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-simple-event-collector.png
--------------------------------------------------------------------------------
/images/lambda-simple-event-env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-simple-event-env.png
--------------------------------------------------------------------------------
/images/lambda-simple-events-designer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-simple-events-designer.png
--------------------------------------------------------------------------------
/images/lambda-trigger-confgure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/lambda-trigger-confgure.png
--------------------------------------------------------------------------------
/images/media-convert-start-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/media-convert-start-0.png
--------------------------------------------------------------------------------
/images/mediaconvert-burnin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-burnin.png
--------------------------------------------------------------------------------
/images/mediaconvert-captions-burn-in-mp4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-captions-burn-in-mp4.png
--------------------------------------------------------------------------------
/images/mediaconvert-captions-burnin-job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-captions-burnin-job.png
--------------------------------------------------------------------------------
/images/mediaconvert-captions-selector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-captions-selector.png
--------------------------------------------------------------------------------
/images/mediaconvert-captions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-captions.png
--------------------------------------------------------------------------------
/images/mediaconvert-clip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-clip.png
--------------------------------------------------------------------------------
/images/mediaconvert-create-job-input1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-create-job-input1.png
--------------------------------------------------------------------------------
/images/mediaconvert-frame-capture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-frame-capture.png
--------------------------------------------------------------------------------
/images/mediaconvert-hls-3-outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-hls-3-outputs.png
--------------------------------------------------------------------------------
/images/mediaconvert-hls-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-hls-settings.png
--------------------------------------------------------------------------------
/images/mediaconvert-input-clipping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-input-clipping.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-Q.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-admarker-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-admarker-Q.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-admarker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-admarker.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-burnin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-burnin.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-captions-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-captions-Q.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-captions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-captions.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-clip-stitch-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-clip-stitch-Q.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-clip-stitch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-clip-stitch.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-mt2ts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-mt2ts.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-settings.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-summary.png
--------------------------------------------------------------------------------
/images/mediaconvert-job-timecode-burnin-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job-timecode-burnin-Q.png
--------------------------------------------------------------------------------
/images/mediaconvert-job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-job.png
--------------------------------------------------------------------------------
/images/mediaconvert-json-output-groups.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-json-output-groups.png
--------------------------------------------------------------------------------
/images/mediaconvert-mp4-output-group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-mp4-output-group.png
--------------------------------------------------------------------------------
/images/mediaconvert-save-jobid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-save-jobid.png
--------------------------------------------------------------------------------
/images/mediaconvert-start-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-start-0.png
--------------------------------------------------------------------------------
/images/mediaconvert-stitch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-stitch.png
--------------------------------------------------------------------------------
/images/mediaconvert-timecode-burn-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-timecode-burn-in.png
--------------------------------------------------------------------------------
/images/mediaconvert-timecode-watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-timecode-watermark.png
--------------------------------------------------------------------------------
/images/mediaconvert-watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediaconvert-watermark.png
--------------------------------------------------------------------------------
/images/mediainfo-dynamodb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediainfo-dynamodb.png
--------------------------------------------------------------------------------
/images/mediainfo-lambda-role.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediainfo-lambda-role.png
--------------------------------------------------------------------------------
/images/mediainfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediainfo.png
--------------------------------------------------------------------------------
/images/mediatailor-configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/mediatailor-configuration.png
--------------------------------------------------------------------------------
/images/module-2-fin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-2-fin.png
--------------------------------------------------------------------------------
/images/module-2-s3-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-2-s3-Q.png
--------------------------------------------------------------------------------
/images/module-2-s3-link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-2-s3-link.png
--------------------------------------------------------------------------------
/images/module-2-s3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-2-s3.png
--------------------------------------------------------------------------------
/images/module-3-job-fin-Q.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-3-job-fin-Q.png
--------------------------------------------------------------------------------
/images/module-3-job-fin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-3-job-fin.png
--------------------------------------------------------------------------------
/images/module-4-hls-timecode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-4-hls-timecode.png
--------------------------------------------------------------------------------
/images/module-4-mp4-watermark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-4-mp4-watermark.png
--------------------------------------------------------------------------------
/images/module-5-fin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-5-fin.png
--------------------------------------------------------------------------------
/images/module-6-fin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/module-6-fin.png
--------------------------------------------------------------------------------
/images/play-test-video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/play-test-video.png
--------------------------------------------------------------------------------
/images/role-link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/role-link.png
--------------------------------------------------------------------------------
/images/s3-create-watchfolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/s3-create-watchfolder.png
--------------------------------------------------------------------------------
/images/segment-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/segment-settings.png
--------------------------------------------------------------------------------
/images/simple-events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/simple-events.png
--------------------------------------------------------------------------------
/images/simple-watchfolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/simple-watchfolder.png
--------------------------------------------------------------------------------
/images/sns-create-topic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/sns-create-topic.png
--------------------------------------------------------------------------------
/images/sns-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/sns-email.png
--------------------------------------------------------------------------------
/images/sns-subsciption.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/sns-subsciption.png
--------------------------------------------------------------------------------
/images/test-mediainfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/test-mediainfo.png
--------------------------------------------------------------------------------
/images/transcribe-job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/transcribe-job.png
--------------------------------------------------------------------------------
/images/verify-watchfolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/verify-watchfolder.png
--------------------------------------------------------------------------------
/images/watchfolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/watchfolder.png
--------------------------------------------------------------------------------
/images/watchfoldere2e.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-media-services-simple-vod-workflow/d0238bb09ad6fabdbb918d5f86705192fa1636b2/images/watchfoldere2e.png
--------------------------------------------------------------------------------