├── architecture.png ├── source ├── cdk │ ├── .npmignore │ ├── jest.config.js │ ├── .gitignore │ ├── cdk.json │ ├── tsconfig.json │ ├── package.json │ ├── bin │ │ └── vod.ts │ └── test │ │ └── vod.test.ts ├── mediainfo │ ├── .coveragerc │ ├── bin │ │ └── LICENSE │ ├── test_compact.py │ ├── README.md │ └── setup.py ├── dynamo │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ ├── error.js │ │ └── index.spec.js │ └── index.js ├── encode │ ├── jest.config.js │ ├── package.json │ └── lib │ │ └── error.js ├── profiler │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ ├── error.js │ │ └── index.spec.js │ └── index.js ├── archive-source │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ ├── error.js │ │ └── index.spec.js │ └── index.js ├── error-handler │ ├── jest.config.js │ ├── package.json │ └── lib │ │ └── index.spec.js ├── input-validate │ ├── jest.config.js │ ├── package.json │ └── lib │ │ └── error.js ├── sqs-publish │ ├── jest.config.js │ ├── package.json │ ├── index.js │ └── lib │ │ ├── error.js │ │ └── index.spec.js ├── step-functions │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ ├── error.js │ │ └── index.spec.js │ └── index.js ├── output-validate │ ├── jest.config.js │ ├── package.json │ └── lib │ │ ├── error.js │ │ └── test-events.js ├── sns-notification │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ └── error.js │ └── index.js ├── media-package-assets │ ├── jest.config.js │ ├── package.json │ ├── lib │ │ └── error.js │ └── index.js └── custom-resource │ ├── package.json │ └── lib │ ├── mediaconvert │ ├── presets │ │ ├── _Ott_Dash_Mp4_Avc_16x9_480x270p_0.4Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_960x540p_3.5Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_1280x720p_3.5Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_1280x720p_5.0Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_1280x720p_6.5Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_640x360p_0.6Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_640x360p_1.2Mbps_qvbr.json │ │ ├── _Ott_Dash_Mp4_Avc_16x9_1920x1080p_8.5Mbps_qvbr.json │ │ ├── _Mp4_Hevc_Aac_16x9_3840x2160p_20Mbps.json │ │ ├── _Mp4_Avc_Aac_16x9_1280x720p_4.5Mbps_qvbr.json │ │ ├── _Mp4_Avc_Aac_16x9_1920x1080p_6Mbps_qvbr.json │ │ ├── _Mp4_Hevc_Aac_16x9_3840x2160p_20Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr.json │ │ ├── _Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr.json │ │ └── _Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr.json │ └── templates │ │ ├── 720p_avc_aac_16x9.json │ │ ├── 720p_avc_aac_16x9_mvod.json │ │ ├── 720p_avc_aac_16x9_qvbr.json │ │ ├── 1080p_avc_aac_16x9.json │ │ ├── 2160p_avc_aac_16x9.json │ │ ├── 1080p_avc_aac_16x9_mvod.json │ │ ├── 1080p_avc_aac_16x9_qvbr.json │ │ ├── 2160p_avc_aac_16x9_mvod.json │ │ └── 2160p_avc_aac_16x9_qvbr.json │ ├── mediapackage │ └── test-assets.js │ ├── metrics │ ├── index.js │ └── index.spec.js │ ├── cfn │ ├── index.js │ └── index.spec.js │ └── s3 │ ├── index.spec.js │ └── index.js ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── solution-manifest.yaml ├── CODE_OF_CONDUCT.md ├── deployment ├── cdk-solution-helper │ ├── package-lock.json │ ├── package.json │ └── index.js └── run-unit-tests.sh ├── SECURITY.md ├── .gitignore └── CONTRIBUTING.md /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-solutions/video-on-demand-on-aws/main/architecture.png -------------------------------------------------------------------------------- /source/cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /source/mediainfo/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | relative_files = True 3 | source = . 4 | omit = 5 | setup.py 6 | test_*.py -------------------------------------------------------------------------------- /source/cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /source/cdk/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vscode/ 3 | *.js 4 | !jest.config.js 5 | *.d.ts 6 | node_modules 7 | 8 | # CDK asset staging directory 9 | .cdk.staging 10 | cdk.out 11 | 12 | # Parcel default cache directory 13 | .parcel-cache -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 6 | -------------------------------------------------------------------------------- /source/cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/vod.ts", 3 | "context": { 4 | "aws-cdk:enableDiffNoFail": "true", 5 | "@aws-cdk/core:stackRelativeExports": "true", 6 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /source/dynamo/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/encode/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/profiler/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /solution-manifest.yaml: -------------------------------------------------------------------------------- 1 | id: SO0021 2 | name: video-on-demand-on-aws 3 | version: v6.1.14 4 | cloudformation_templates: 5 | - template: video-on-demand-on-aws.template 6 | main_template: true 7 | build_environment: 8 | build_image: "aws/codebuild/standard:7.0" 9 | -------------------------------------------------------------------------------- /source/archive-source/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/error-handler/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/input-validate/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/sqs-publish/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/step-functions/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/output-validate/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/sns-notification/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /source/media-package-assets/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/lib'], 3 | testMatch: ['**/*.spec.js'], 4 | coveragePathIgnorePatterns: ['/lib/utils.test.js'], 5 | coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] 6 | }; -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | 3 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 4 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 5 | opensource-codeofconduct@amazon.com with any additional questions or comments. 6 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "cdk-solution-helper", 9 | "version": "0.1.0", 10 | "license": "Apache-2.0" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk-solution-helper", 3 | "description": "CDK Generated template post processor", 4 | "license": "Apache-2.0", 5 | "version": "0.1.0", 6 | "author": { 7 | "name": "Amazon Web Services", 8 | "url": "https://aws.amazon.com/solutions" 9 | }, 10 | "dependencies": { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting Security Issues 2 | 3 | We take all security reports seriously. 4 | When we receive such reports, 5 | we will investigate and subsequently address 6 | any potential vulnerabilities as quickly as possible. 7 | If you discover a potential security issue in this project, 8 | please notify AWS/Amazon Security via our 9 | [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) 10 | or directly via email to [AWS Security](mailto:aws-security@amazon.com). 11 | Please do *not* create a public GitHub issue in this project. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this solution 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the feature you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | **/dist 3 | **/global-s3-assets 4 | **/regional-s3-assets 5 | **/open-source 6 | **/.zip 7 | /build 8 | 9 | # python output 10 | **/__pycache__ 11 | **/pytests 12 | **/*.egg-info 13 | **/*.pyc 14 | **/pypackage 15 | 16 | # dependencies 17 | **/node_modules 18 | 19 | # misc 20 | **/npm-debug.log 21 | **/.vscode/settings.json 22 | codescan-funcs.sh 23 | codescan-postbuild-default.sh 24 | codescan-prebuild-default.sh 25 | codescan-unified-default.sh 26 | 27 | # System Files 28 | **/.DS_Store 29 | **/.vscode 30 | 31 | # test coverage files 32 | **/source/test 33 | **source/**/.coverage 34 | **source/**/coverage 35 | -------------------------------------------------------------------------------- /source/archive-source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-archive-source", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "ingest workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-s3": "^3.622.0", 15 | "@aws-sdk/client-lambda": "^3.622.0", 16 | "aws-sdk-client-mock": "^3.0.0", 17 | "chai": "^4.2.0", 18 | "jest": "^29.6.3" 19 | }, 20 | "private": true, 21 | "author": { 22 | "name": "Amazon Web Services", 23 | "url": "https://aws.amazon.com/solutions" 24 | }, 25 | "license": "Apache-2.0" 26 | } 27 | -------------------------------------------------------------------------------- /source/sqs-publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-notification", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "sqs function for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-lambda": "^3.622.0", 15 | "@aws-sdk/client-sqs": "^3.622.0", 16 | "aws-sdk-client-mock": "^3.0.0", 17 | "chai": "^4.2.0", 18 | "jest": "^29.6.3", 19 | "sinon": "^17.0.0" 20 | }, 21 | "private": true, 22 | "author": { 23 | "name": "Amazon Web Services", 24 | "url": "https://aws.amazon.com/solutions" 25 | }, 26 | "license": "Apache-2.0" 27 | } 28 | -------------------------------------------------------------------------------- /source/sns-notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-notification", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "sns function for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-lambda": "^3.622.0", 15 | "@aws-sdk/client-sns": "^3.622.0", 16 | "aws-sdk-client-mock": "^3.0.0", 17 | "chai": "^4.2.0", 18 | "jest": "^29.6.3", 19 | "sinon": "^17.0.0" 20 | }, 21 | "private": true, 22 | "author": { 23 | "name": "Amazon Web Services", 24 | "url": "https://aws.amazon.com/solutions" 25 | }, 26 | "license": "Apache-2.0" 27 | } 28 | -------------------------------------------------------------------------------- /source/media-package-assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-media-package-asset", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "ingest function for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-lambda": "^3.622.0", 15 | "@aws-sdk/client-mediapackage-vod": "^3.622.0", 16 | "aws-sdk-client-mock": "^3.0.0", 17 | "chai": "^4.2.0", 18 | "jest": "^29.6.3", 19 | "sinon": "^17.0.0" 20 | }, 21 | "private": true, 22 | "author": { 23 | "name": "Amazon Web Services", 24 | "url": "https://aws.amazon.com/solutions" 25 | }, 26 | "license": "Apache-2.0" 27 | } 28 | -------------------------------------------------------------------------------- /source/dynamo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-dynamo", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "dynamo helper for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-lambda": "^3.622.0", 15 | "@aws-sdk/lib-dynamodb": "^3.622.0", 16 | "@aws-sdk/client-dynamodb": "^3.622.0", 17 | "aws-sdk-client-mock": "^3.0.0", 18 | "chai": "^4.2.0", 19 | "jest": "^29.6.3", 20 | "sinon": "^17.0.0" 21 | }, 22 | "private": true, 23 | "author": { 24 | "name": "Amazon Web Services", 25 | "url": "https://aws.amazon.com/solutions" 26 | }, 27 | "license": "Apache-2.0" 28 | } 29 | -------------------------------------------------------------------------------- /source/input-validate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-input-validation", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "ingest workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "dependencies": { 14 | }, 15 | "devDependencies": { 16 | "@aws-sdk/client-s3": "^3.622.0", 17 | "@aws-sdk/client-lambda": "^3.622.0", 18 | "aws-sdk-client-mock": "^3.0.0", 19 | "chai": "^4.2.0", 20 | "jest": "^29.6.3", 21 | "sinon": "^17.0.0" 22 | }, 23 | "private": true, 24 | "author": { 25 | "name": "Amazon Web Services", 26 | "url": "https://aws.amazon.com/solutions" 27 | }, 28 | "license": "Apache-2.0" 29 | } 30 | -------------------------------------------------------------------------------- /source/cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /source/profiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-profiler", 3 | "version": "1.0.0", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "process workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/client-lambda": "^3.622.0", 15 | "@aws-sdk/lib-dynamodb": "^3.622.0", 16 | "@aws-sdk/client-dynamodb": "^3.622.0", 17 | "aws-sdk-client-mock": "^3.0.0", 18 | "chai": "^4.2.0", 19 | "jest": "^29.6.3", 20 | "sinon": "^17.0.0" 21 | }, 22 | "private": true, 23 | "author": { 24 | "name": "Amazon Web Services", 25 | "url": "https://aws.amazon.com/solutions" 26 | }, 27 | "license": "Apache-2.0" 28 | } 29 | -------------------------------------------------------------------------------- /source/error-handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-error-handler", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "error handler for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "devDependencies": { 14 | "@aws-sdk/lib-dynamodb": "^3.622.0", 15 | "@aws-sdk/client-dynamodb": "^3.622.0", 16 | "@aws-sdk/client-sns": "^3.622.0", 17 | "aws-sdk-client-mock": "^3.0.0", 18 | "chai": "^4.2.0", 19 | "jest": "^29.6.3", 20 | "sinon": "^17.0.0" 21 | }, 22 | "private": true, 23 | "author": { 24 | "name": "Amazon Web Services", 25 | "url": "https://aws.amazon.com/solutions" 26 | }, 27 | "license": "Apache-2.0" 28 | } 29 | -------------------------------------------------------------------------------- /source/encode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-encode", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "process workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "dependencies": { 14 | "lodash": "^4.17.15" 15 | }, 16 | "devDependencies": { 17 | "@aws-sdk/client-lambda": "^3.622.0", 18 | "@aws-sdk/client-mediaconvert": "^3.622.0", 19 | "aws-sdk-client-mock": "^3.0.0", 20 | "chai": "^4.2.0", 21 | "jest": "^29.6.3", 22 | "sinon": "^17.0.0" 23 | }, 24 | "private": true, 25 | "author": { 26 | "name": "Amazon Web Services", 27 | "url": "https://aws.amazon.com/solutions" 28 | }, 29 | "license": "Apache-2.0" 30 | } 31 | -------------------------------------------------------------------------------- /source/step-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-step-functions", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "ingest workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "dependencies": { 14 | "uuid": "^9.0.0" 15 | }, 16 | "devDependencies": { 17 | "@aws-sdk/client-lambda": "^3.622.0", 18 | "@aws-sdk/client-sfn": "^3.622.0", 19 | "aws-sdk-client-mock": "^3.0.0", 20 | "chai": "^4.2.0", 21 | "jest": "^29.6.3", 22 | "sinon": "^17.0.0" 23 | }, 24 | "private": true, 25 | "author": { 26 | "name": "Amazon Web Services", 27 | "url": "https://aws.amazon.com/solutions" 28 | }, 29 | "license": "Apache-2.0" 30 | } 31 | -------------------------------------------------------------------------------- /source/output-validate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-output-validation", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "ingest workflow for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "dependencies": { 14 | "uuid": "^9.0.0" 15 | }, 16 | "devDependencies": { 17 | "@aws-sdk/client-s3": "^3.622.0", 18 | "@aws-sdk/client-lambda": "^3.622.0", 19 | "@aws-sdk/lib-dynamodb": "^3.622.0", 20 | "@aws-sdk/client-dynamodb": "^3.622.0", 21 | "aws-sdk-client-mock": "^3.0.0", 22 | "chai": "^4.2.0", 23 | "jest": "^29.6.3", 24 | "sinon": "^17.0.0" 25 | }, 26 | "private": true, 27 | "author": { 28 | "name": "Amazon Web Services", 29 | "url": "https://aws.amazon.com/solutions" 30 | }, 31 | "license": "Apache-2.0" 32 | } 33 | -------------------------------------------------------------------------------- /source/custom-resource/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vod-custom-resource", 3 | "version": "6.1.14", 4 | "engines": { 5 | "node": ">=22" 6 | }, 7 | "description": "cfn custom resources for video on demand", 8 | "main": "index.js", 9 | "scripts": { 10 | "pretest": "npm i --quiet", 11 | "test": "jest --coverage" 12 | }, 13 | "dependencies": { 14 | "axios": "^1.12.0", 15 | "uuid": "^9.0.0" 16 | }, 17 | "devDependencies": { 18 | "@aws-sdk/client-s3": "^3.622.0", 19 | "@aws-sdk/client-lambda": "^3.622.0", 20 | "@aws-sdk/client-cloudfront": "^3.622.0", 21 | "@aws-sdk/client-mediaconvert": "^3.622.0", 22 | "@aws-sdk/client-mediapackage-vod": "^3.622.0", 23 | "aws-sdk-client-mock": "^3.0.0", 24 | "axios-mock-adapter": "^2.1.0", 25 | "chai": "^4.2.0", 26 | "jest": "^29.6.3", 27 | "lodash": "^4.17.15", 28 | "sinon": "^17.0.0" 29 | }, 30 | "private": true, 31 | "author": { 32 | "name": "Amazon Web Services", 33 | "url": "https://aws.amazon.com/solutions" 34 | }, 35 | "license": "Apache-2.0" 36 | } 37 | -------------------------------------------------------------------------------- /source/cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "video-on-demand-on-aws", 3 | "description": "Synthesize templates for Video on Demand on AWS using AWS Cloud Development Kit (CDK).", 4 | "license": "Apache-2.0", 5 | "version": "6.1.14", 6 | "bin": { 7 | "cdk": "bin/vod.js" 8 | }, 9 | "author": { 10 | "name": "Amazon Web Services", 11 | "url": "https://aws.amazon.com/solutions" 12 | }, 13 | "scripts": { 14 | "build": "tsc", 15 | "watch": "tsc -w", 16 | "test": "jest --coverage", 17 | "cdk": "cdk" 18 | }, 19 | "devDependencies": { 20 | "@aws-cdk/assert": "2.68.0", 21 | "@types/jest": "^29.5.4", 22 | "@types/node": "^20.5.3", 23 | "@types/prettier": "^3.0.0", 24 | "jest": "^29.6.3", 25 | "ts-jest": "^29.1.1", 26 | "aws-cdk": "^2.196.0", 27 | "ts-node": "^10.9.1", 28 | "typescript": "^5.1.6" 29 | }, 30 | "dependencies": { 31 | "aws-cdk-lib": "^2.196.0", 32 | "@aws-solutions-constructs/aws-cloudfront-s3": "^2.85.2", 33 | "constructs": "^10.2.47", 34 | "source-map-support": "^0.5.21", 35 | "cdk-nag": "^2.27.32" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/mediainfo/bin/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2002-2020, MediaArea.net SARL 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Please complete the following information about the solution:** 20 | - [ ] Version: [e.g. v1.0.0] 21 | 22 | To get the version of the solution, you can look at the description of the created CloudFormation stack. For example, "_(SO0021) - Video On Demand workflow with AWS Step Functions, MediaConvert, MediaPackage, S3, CloudFront and DynamoDB. Version **v5.0.0**_". If the description does not contain the version information, you can look at the mappings section of the template: 23 | 24 | ```yaml 25 | Mappings: 26 | SourceCode: 27 | General: 28 | S3Bucket: "solutions" 29 | KeyPrefix: "video-on-demand-on-aws/v5.0.0" 30 | ``` 31 | 32 | - [ ] Region: [e.g. us-east-1] 33 | - [ ] Was the solution modified from the version published on this repository? 34 | - [ ] If the answer to the previous question was yes, are the changes available on GitHub? 35 | - [ ] Have you checked your [service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) for the sevices this solution uses? 36 | - [ ] Were there any errors in the CloudWatch Logs? 37 | 38 | **Screenshots** 39 | If applicable, add screenshots to help explain your problem (please **DO NOT include sensitive information**). 40 | 41 | **Additional context** 42 | Add any other context about the problem here. 43 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_480x270p_0.4Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 400000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "MEDIUM", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 45.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 800000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 270, 47 | "Width": 480, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_960x540p_3.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 3500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 7000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 540, 47 | "Width": 960, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_1280x720p_3.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 3500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 7000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_1280x720p_5.0Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 5000000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 10000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_1280x720p_6.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 6500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 13000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_640x360p_0.6Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 600000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "MEDIUM", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 1200000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 360, 47 | "Width": 640, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_640x360p_1.2Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 1200000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "MEDIUM", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 2400000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 360, 47 | "Width": 640, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Dash_Mp4_Avc_16x9_1920x1080p_8.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 8500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "DISABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 1, 39 | "HrdBufferSize": 17000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 1080, 47 | "Width": 1920, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "ContainerSettings": { 54 | "Container": "MPD" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/cdk/bin/vod.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /********************************************************************************************************************* 3 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 4 | * * 5 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 6 | * with the License. A copy of the License is located at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 11 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 12 | * and limitations under the License. * 13 | *********************************************************************************************************************/ 14 | 15 | import 'source-map-support/register'; 16 | import * as cdk from 'aws-cdk-lib'; 17 | import { DefaultStackSynthesizer } from 'aws-cdk-lib'; 18 | import { VideoOnDemand } from '../lib/vod-stack'; 19 | import { AwsSolutionsChecks } from 'cdk-nag'; 20 | 21 | const app = new cdk.App(); 22 | new VideoOnDemand(app, 'VideoOnDemand', { // NOSONAR 23 | synthesizer: new DefaultStackSynthesizer({ 24 | generateBootstrapVersionRule: false 25 | }) 26 | }); // NOSONAR 27 | 28 | //cdk nag 29 | cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true })); -------------------------------------------------------------------------------- /source/custom-resource/lib/mediapackage/test-assets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | const ConfigurationWithS3 = { 7 | ETag: 'some-etag', 8 | DistributionConfig: { 9 | CallerReference: 'some-caller-reference', 10 | Origins: { 11 | Quantity: 1, 12 | Items: [{ 13 | Id: 's3Origin', 14 | DomainName: 'some-bucket.s3.us-east-1.amazonaws.com', 15 | OriginPath: '', 16 | CustomHeaders: { 17 | Quantity: 0 18 | }, 19 | S3OriginConfig: { 20 | OriginAccessIdentity: 'origin-access-identity/cloudfront/some-oai' 21 | } 22 | }] 23 | }, 24 | DefaultCacheBehavior: { 25 | TargetOriginId: 's3Origin', 26 | ForwardedValues: { 27 | QueryString: false, 28 | Cookies: { Forward: 'none' }, 29 | Headers: { 30 | Quantity: 3, 31 | Items: [ 32 | 'Access-Control-Request-Headers', 33 | 'Access-Control-Request-Method', 34 | 'Origin' 35 | ] 36 | }, 37 | QueryStringCacheKeys: { Quantity: 0 } 38 | }, 39 | TrustedSigners: { Enabled: false, Quantity: 0 }, 40 | ViewerProtocolPolicy: 'allow-all', 41 | MinTTL: 0 42 | }, 43 | CacheBehaviors: { Quantity: 0 }, 44 | Comment: '', 45 | Enabled: true 46 | } 47 | }; 48 | 49 | const ConfigurationWithMP = { 50 | DistributionConfig: { 51 | Origins: { 52 | Items: [{ Id: 'vodMPOrigin' }], 53 | Quantity: 1 54 | } 55 | } 56 | }; 57 | 58 | module.exports = { 59 | ConfigurationWithS3, 60 | ConfigurationWithMP, 61 | DistributionId: 'distribution-id', 62 | DomainName: 'https://random-id.egress.mediapackage-vod.us-east-1.amazonaws.com' 63 | }; 64 | -------------------------------------------------------------------------------- /source/sqs-publish/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { SQS } = require("@aws-sdk/client-sqs"); 15 | const error = require('./lib/error.js'); 16 | 17 | 18 | exports.handler = async (event) => { 19 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 20 | 21 | const sqs = new SQS({ 22 | region: process.env.AWS_REGION, 23 | customUserAgent: process.env.SOLUTION_IDENTIFIER 24 | }); 25 | 26 | try { 27 | 28 | console.log(`SEND SQS:: ${JSON.stringify(event, null, 2)}`); 29 | 30 | let params = { 31 | MessageBody: JSON.stringify(event, null, 2), 32 | QueueUrl: process.env.SqsQueue 33 | }; 34 | 35 | await sqs.sendMessage(params); 36 | 37 | } catch (err) { 38 | await error.handler(event, err); 39 | throw err; 40 | } 41 | 42 | return event; 43 | }; 44 | -------------------------------------------------------------------------------- /source/encode/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/profiler/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/archive-source/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/input-validate/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/output-validate/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/sns-notification/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/sqs-publish/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | "guid": event.guid, 24 | "event": event, 25 | "function": process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | "error": _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/step-functions/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/dynamo/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { 15 | Lambda 16 | } = require("@aws-sdk/client-lambda"); 17 | 18 | let errHandler = async (event, _err) => { 19 | const lambda = new Lambda({ 20 | region: process.env.AWS_REGION 21 | }); 22 | 23 | try { 24 | let payload = { 25 | 'guid': event.guid, 26 | 'event': event, 27 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 28 | 'error': _err.toString() 29 | }; 30 | 31 | let params = { 32 | FunctionName: process.env.ErrorHandler, 33 | Payload: JSON.stringify(payload, null, 2) 34 | }; 35 | 36 | await lambda.invoke(params); 37 | } catch (err) { 38 | console.log(err); 39 | throw err; 40 | } 41 | 42 | return 'success'; 43 | }; 44 | 45 | module.exports = { 46 | handler: errHandler 47 | }; 48 | -------------------------------------------------------------------------------- /source/media-package-assets/lib/error.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { Lambda } = require("@aws-sdk/client-lambda"); 15 | 16 | let errHandler = async (event, _err) => { 17 | const lambda = new Lambda({ 18 | region: process.env.AWS_REGION 19 | }); 20 | 21 | try { 22 | let payload = { 23 | 'guid': event.guid, 24 | 'event': event, 25 | 'function': process.env.AWS_LAMBDA_FUNCTION_NAME, 26 | 'error': _err.toString() 27 | }; 28 | 29 | let params = { 30 | FunctionName: process.env.ErrorHandler, 31 | Payload: JSON.stringify(payload, null, 2) 32 | }; 33 | 34 | await lambda.invoke(params); 35 | } catch (err) { 36 | console.log(err); 37 | throw err; 38 | } 39 | 40 | return 'success'; 41 | }; 42 | 43 | module.exports = { 44 | handler: errHandler 45 | }; 46 | -------------------------------------------------------------------------------- /source/mediainfo/test_compact.py: -------------------------------------------------------------------------------- 1 | ###################################################################################################################### 2 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 3 | # # 4 | # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # 5 | # with the License. A copy of the License is located at # 6 | # # 7 | # http://www.apache.org/licenses/LICENSE-2.0 # 8 | # # 9 | # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES # 10 | # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # 11 | # and limitations under the License. # 12 | ###################################################################################################################### 13 | 14 | import unittest 15 | import lambda_function as function 16 | 17 | class TestCompact(unittest.TestCase): 18 | def test_removes_empty_attributes(self): 19 | attributes = { 20 | 'format': 'mp4', 21 | 'fileSize': 1000, 22 | 'duration': None 23 | } 24 | 25 | expected = { 26 | 'format': 'mp4', 27 | 'fileSize': 1000 28 | } 29 | 30 | self.assertEqual(function.compact(attributes), expected) 31 | 32 | def test_does_not_remove_zero_attributes(self): 33 | attributes = { 34 | 'format': 'mp4', 35 | 'fileSize': 0 36 | } 37 | 38 | expected = { 39 | 'format': 'mp4', 40 | 'fileSize': 0 41 | } 42 | 43 | self.assertEqual(function.compact(attributes), expected) 44 | 45 | if __name__ == '__main__': 46 | unittest.main() 47 | -------------------------------------------------------------------------------- /source/cdk/test/vod.test.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | import '@aws-cdk/assert/jest'; 15 | import { Stack } from 'aws-cdk-lib'; 16 | import { SynthUtils } from '@aws-cdk/assert'; 17 | import * as VideoOnDemand from '../lib/vod-stack'; 18 | 19 | expect.addSnapshotSerializer({ 20 | test: (val) => typeof val === 'string', 21 | print: (val) => { 22 | const valueReplacements = [ 23 | { 24 | regex: /([A-Fa-f0-9]{64}).zip/, 25 | replacementValue: '[HASH REMOVED].zip' 26 | } 27 | ]; 28 | 29 | return `${valueReplacements.reduce( 30 | (output, replacement) => output.replace(replacement.regex, replacement.replacementValue), 31 | val as string 32 | )}`; 33 | } 34 | }); 35 | 36 | test('VideoOnDemand Stack Test', () => { 37 | const stack = new Stack(); 38 | const vodTest = new VideoOnDemand.VideoOnDemand(stack, 'VideoOnDemand'); 39 | expect(SynthUtils.toCloudFormation(vodTest)).toMatchSnapshot(); 40 | }); -------------------------------------------------------------------------------- /source/custom-resource/lib/metrics/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const axios = require('axios'); 15 | 16 | const sanitizeData = (config) => { 17 | // Remove lambda arn from config to avoid sending AccountId 18 | delete config['ServiceToken']; 19 | delete config['Resource']; 20 | 21 | return config; 22 | }; 23 | 24 | const send = async (config) => { 25 | let data; 26 | 27 | const metrics = { 28 | Solution: config.SolutionId, 29 | UUID: config.UUID, 30 | TimeStamp: new Date().toISOString(), 31 | Data: sanitizeData(config) 32 | }; 33 | 34 | const params = { 35 | method: 'post', 36 | port: 443, 37 | url: 'https://metrics.awssolutionsbuilder.com/generic', 38 | headers: { 39 | 'Content-Type': 'application/json' 40 | }, 41 | data: metrics 42 | }; 43 | 44 | data = await axios(params); 45 | 46 | return data.status; 47 | }; 48 | 49 | module.exports = { 50 | send, 51 | sanitizeData 52 | }; 53 | -------------------------------------------------------------------------------- /source/custom-resource/lib/cfn/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const axios = require('axios'); 15 | 16 | let sendResponse = async (event, context, responseStatus, responseData) => { 17 | let data; 18 | 19 | const responseBody = JSON.stringify({ 20 | Status: responseStatus, 21 | Reason: 'See the details in CloudWatch Log Stream: ' + context.logStreamName, 22 | PhysicalResourceId: event.LogicalResourceId, 23 | StackId: event.StackId, 24 | RequestId: event.RequestId, 25 | LogicalResourceId: event.LogicalResourceId, 26 | Data: responseData 27 | }); 28 | 29 | const params = { 30 | url: event.ResponseURL, 31 | port: 443, 32 | method: 'put', 33 | headers: { 34 | 'content-type': '', 35 | 'content-length': responseBody.length 36 | }, 37 | data: responseBody 38 | }; 39 | 40 | data = await axios(params); 41 | 42 | return data.status; 43 | }; 44 | 45 | module.exports = { 46 | send: sendResponse 47 | }; 48 | -------------------------------------------------------------------------------- /source/archive-source/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { S3 } = require("@aws-sdk/client-s3"); 15 | const error = require('./lib/error.js'); 16 | 17 | exports.handler = async (event) => { 18 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 19 | 20 | const s3 = new S3(); 21 | 22 | try { 23 | let params = { 24 | Bucket: event.srcBucket, 25 | Key: event.srcVideo, 26 | Tagging: { 27 | TagSet: [ 28 | { 29 | Key: 'guid', 30 | Value: event.guid 31 | }, 32 | { 33 | Key: process.env.AWS_LAMBDA_FUNCTION_NAME.slice(0, -15), 34 | Value: event.archiveSource 35 | } 36 | ] 37 | } 38 | }; 39 | 40 | await s3.putObjectTagging(params); 41 | } catch (err) { 42 | await error.handler(event, err); 43 | return err; 44 | } 45 | 46 | return event; 47 | }; 48 | -------------------------------------------------------------------------------- /source/mediainfo/README.md: -------------------------------------------------------------------------------- 1 | The version of MediaInfo used by this solution is v20.09. If you want to use a different version, you can compile the source code using these commands (taken from this [blog post](https://aws.amazon.com/blogs/compute/extracting-video-metadata-using-lambda-and-mediainfo/)) as a reference: 2 | 3 | ```console 4 | sudo yum update -y 5 | sudo yum groupinstall 'Development Tools' -y 6 | sudo yum install libcurl-devel -y 7 | wget https://mediaarea.net/download/binary/mediainfo/19.09/MediaInfo_CLI_20.09_GNU_FromSource.tar.xz 8 | tar xvf MediaInfo_CLI_20.09_GNU_FromSource.tar.xz 9 | cd MediaInfo_CLI_GNU_FromSource/ 10 | ./CLI_Compile.sh --with-libcurl 11 | ``` 12 | 13 | Run these commands to confirm the compilation was successful: 14 | 15 | ```console 16 | cd MediaInfo/Project/GNU/CLI/ 17 | ./mediainfo --version 18 | ``` 19 | 20 | *** 21 | 22 | With the update of MediaInfo (from v0.7.92.1 to v20.09), you might run into some issues if you depend on the metadata report format. Here's an example for a mp4 file: 23 | 24 | ```diff 25 | { 26 | "filename": "video.mp4", 27 | "container": { 28 | "format": "MPEG-4", 29 | - "mimeType": "video/mp4", 30 | "fileSize": 22145829, 31 | - "duration": 21021, 32 | + "duration": 21.021, 33 | "totalBitrate": 8428078 34 | }, 35 | "video": [ 36 | { 37 | "codec": "AVC", 38 | "profile": "High@L4", 39 | "bitrate": 8000000, 40 | - "duration": 21021, 41 | + "duration": 21.021, 42 | "frameCount": 630, 43 | "width": 1920, 44 | "height": 1080, 45 | "framerate": 29.97, 46 | "scanType": "Progressive", 47 | - "aspectRatio": "16:9", 48 | + "aspectRatio": "1.778", 49 | "bitDepth": 8, 50 | "colorSpace": "YUV 4:2:0" 51 | } 52 | ], 53 | "audio": [ 54 | { 55 | "codec": "AAC", 56 | "bitrate": 384000, 57 | - "duration": 21021, 58 | + "duration": 21.021, 59 | "frameCount": 985, 60 | "bitrateMode": "VBR", 61 | "channels": 2, 62 | "samplingRate": 48000, 63 | "samplePerFrame": 1024 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | In summary: 70 | - Some properties, such as _duration_, are now floats instead of integers 71 | - _mimeType_ is not included anymore 72 | - Format for _aspectRatio_ has changed 73 | 74 | For a complete list of changes, please refer to the [MediaInfo change log](https://mediaarea.net/MediaInfo/ChangeLog). -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Mp4_Hevc_Aac_16x9_3840x2160p_20Mbps.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "H265Settings": { 5 | "SpatialAdaptiveQuantization": "ENABLED", 6 | "TemporalAdaptiveQuantization": "ENABLED", 7 | "Tiles": "ENABLED", 8 | "MinIInterval": 0, 9 | "UnregisteredSeiTimecode": "DISABLED", 10 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 11 | "CodecProfile": "MAIN_MAIN", 12 | "Bitrate": 20000000, 13 | "RateControlMode": "CBR", 14 | "GopBReference": "ENABLED", 15 | "GopSizeUnits": "SECONDS", 16 | "FlickerAdaptiveQuantization": "ENABLED", 17 | "Telecine": "NONE", 18 | "ParDenominator": 1, 19 | "AdaptiveQuantization": "HIGH", 20 | "TemporalIds": "DISABLED", 21 | "InterlaceMode": "PROGRESSIVE", 22 | "QualityTuningLevel": "SINGLE_PASS_HQ", 23 | "HrdBufferInitialFillPercentage": 90, 24 | "SlowPal": "DISABLED", 25 | "NumberBFramesBetweenReferenceFrames": 3, 26 | "SampleAdaptiveOffsetFilterMode": "ADAPTIVE", 27 | "GopSize": 2.0, 28 | "ParControl": "SPECIFIED", 29 | "Slices": 4, 30 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 31 | "AlternateTransferFunctionSei": "DISABLED", 32 | "ParNumerator": 1, 33 | "SceneChangeDetect": "ENABLED", 34 | "GopClosedCadence": 1, 35 | "HrdBufferSize": 16000000 36 | }, 37 | "Codec": "H_265" 38 | }, 39 | "DropFrameTimecode": "ENABLED", 40 | "RespondToAfd": "NONE", 41 | "Sharpness": 50, 42 | "AfdSignaling": "NONE", 43 | "Height": 2160, 44 | "Width": 3840, 45 | "ScalingBehavior": "DEFAULT", 46 | "ColorMetadata": "INSERT", 47 | "AntiAlias": "ENABLED", 48 | "TimecodeInsertion": "DISABLED" 49 | }, 50 | "AudioDescriptions": [{ 51 | "CodecSettings": { 52 | "Codec": "AAC", 53 | "AacSettings": { 54 | "CodecProfile": "LC", 55 | "Specification": "MPEG4", 56 | "RateControlMode": "CBR", 57 | "AudioDescriptionBroadcasterMix": "NORMAL", 58 | "SampleRate": 48000, 59 | "Bitrate": 160000, 60 | "CodingMode": "CODING_MODE_2_0", 61 | "RawFormat": "NONE" 62 | } 63 | }, 64 | "AudioSourceName": "Audio Selector 1", 65 | "AudioTypeControl": "FOLLOW_INPUT", 66 | "LanguageCodeControl": "FOLLOW_INPUT", 67 | "AudioType": 0 68 | }], 69 | "ContainerSettings": { 70 | "Mp4Settings": { 71 | "MoovPlacement": "PROGRESSIVE_DOWNLOAD", 72 | "CslgAtom": "INCLUDE", 73 | "FreeSpaceBox": "EXCLUDE" 74 | }, 75 | "Container": "MP4" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Mp4_Avc_Aac_16x9_1280x720p_4.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Softness": 0, 10 | "Syntax": "DEFAULT", 11 | "MinIInterval": 0, 12 | "UnregisteredSeiTimecode": "DISABLED", 13 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 14 | "QvbrSettings": { 15 | "QvbrQualityLevel": 8 16 | }, 17 | "MaxBitrate": 4500000, 18 | "RateControlMode": "QVBR", 19 | "GopBReference": "ENABLED", 20 | "EntropyEncoding": "CABAC", 21 | "GopSizeUnits": "SECONDS", 22 | "FlickerAdaptiveQuantization": "ENABLED", 23 | "Telecine": "NONE", 24 | "ParDenominator": 1, 25 | "AdaptiveQuantization": "HIGH", 26 | "InterlaceMode": "PROGRESSIVE", 27 | "QualityTuningLevel": "SINGLE_PASS_HQ", 28 | "HrdBufferInitialFillPercentage": 90, 29 | "RepeatPps": "DISABLED", 30 | "FieldEncoding": "PAFF", 31 | "SlowPal": "DISABLED", 32 | "GopClosedCadence": 1, 33 | "GopSize": 2.0, 34 | "ParControl": "SPECIFIED", 35 | "Slices": 1, 36 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 37 | "ParNumerator": 1, 38 | "SceneChangeDetect": "ENABLED", 39 | "NumberBFramesBetweenReferenceFrames": 3, 40 | "HrdBufferSize": 9000000 41 | } 42 | }, 43 | "DropFrameTimecode": "ENABLED", 44 | "RespondToAfd": "NONE", 45 | "Sharpness": 50, 46 | "AfdSignaling": "NONE", 47 | "Height": 720, 48 | "Width": 1280, 49 | "ScalingBehavior": "DEFAULT", 50 | "ColorMetadata": "INSERT", 51 | "AntiAlias": "ENABLED", 52 | "TimecodeInsertion": "DISABLED" 53 | }, 54 | "AudioDescriptions": [{ 55 | "CodecSettings": { 56 | "Codec": "AAC", 57 | "AacSettings": { 58 | "CodecProfile": "LC", 59 | "Specification": "MPEG4", 60 | "RateControlMode": "CBR", 61 | "AudioDescriptionBroadcasterMix": "NORMAL", 62 | "SampleRate": 48000, 63 | "Bitrate": 160000, 64 | "CodingMode": "CODING_MODE_2_0", 65 | "RawFormat": "NONE" 66 | } 67 | }, 68 | "AudioSourceName": "Audio Selector 1", 69 | "AudioTypeControl": "FOLLOW_INPUT", 70 | "LanguageCodeControl": "FOLLOW_INPUT", 71 | "AudioType": 0 72 | }], 73 | "ContainerSettings": { 74 | "Mp4Settings": { 75 | "MoovPlacement": "PROGRESSIVE_DOWNLOAD", 76 | "CslgAtom": "INCLUDE", 77 | "FreeSpaceBox": "EXCLUDE" 78 | }, 79 | "Container": "MP4" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Mp4_Avc_Aac_16x9_1920x1080p_6Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Softness": 0, 10 | "Syntax": "DEFAULT", 11 | "MinIInterval": 0, 12 | "UnregisteredSeiTimecode": "DISABLED", 13 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 14 | "QvbrSettings": { 15 | "QvbrQualityLevel": 8 16 | }, 17 | "MaxBitrate": 6000000, 18 | "RateControlMode": "QVBR", 19 | "GopBReference": "DISABLED", 20 | "EntropyEncoding": "CABAC", 21 | "GopSizeUnits": "SECONDS", 22 | "FlickerAdaptiveQuantization": "ENABLED", 23 | "Telecine": "NONE", 24 | "ParDenominator": 1, 25 | "AdaptiveQuantization": "HIGH", 26 | "InterlaceMode": "PROGRESSIVE", 27 | "QualityTuningLevel": "SINGLE_PASS_HQ", 28 | "HrdBufferInitialFillPercentage": 90, 29 | "RepeatPps": "DISABLED", 30 | "FieldEncoding": "PAFF", 31 | "SlowPal": "DISABLED", 32 | "GopClosedCadence": 1, 33 | "GopSize": 2.0, 34 | "ParControl": "SPECIFIED", 35 | "Slices": 1, 36 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 37 | "ParNumerator": 1, 38 | "SceneChangeDetect": "ENABLED", 39 | "NumberBFramesBetweenReferenceFrames": 1, 40 | "HrdBufferSize": 12000000 41 | } 42 | }, 43 | "DropFrameTimecode": "ENABLED", 44 | "RespondToAfd": "NONE", 45 | "Sharpness": 50, 46 | "AfdSignaling": "NONE", 47 | "Height": 1080, 48 | "Width": 1920, 49 | "ScalingBehavior": "DEFAULT", 50 | "ColorMetadata": "INSERT", 51 | "AntiAlias": "ENABLED", 52 | "TimecodeInsertion": "DISABLED" 53 | }, 54 | "AudioDescriptions": [{ 55 | "CodecSettings": { 56 | "Codec": "AAC", 57 | "AacSettings": { 58 | "CodecProfile": "LC", 59 | "Specification": "MPEG4", 60 | "RateControlMode": "CBR", 61 | "AudioDescriptionBroadcasterMix": "NORMAL", 62 | "SampleRate": 48000, 63 | "Bitrate": 160000, 64 | "CodingMode": "CODING_MODE_2_0", 65 | "RawFormat": "NONE" 66 | } 67 | }, 68 | "AudioSourceName": "Audio Selector 1", 69 | "AudioTypeControl": "FOLLOW_INPUT", 70 | "LanguageCodeControl": "FOLLOW_INPUT", 71 | "AudioType": 0 72 | }], 73 | "ContainerSettings": { 74 | "Mp4Settings": { 75 | "MoovPlacement": "PROGRESSIVE_DOWNLOAD", 76 | "CslgAtom": "INCLUDE", 77 | "FreeSpaceBox": "EXCLUDE" 78 | }, 79 | "Container": "MP4" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Mp4_Hevc_Aac_16x9_3840x2160p_20Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "H265Settings": { 5 | "SpatialAdaptiveQuantization": "ENABLED", 6 | "TemporalAdaptiveQuantization": "ENABLED", 7 | "Tiles": "ENABLED", 8 | "MinIInterval": 0, 9 | "UnregisteredSeiTimecode": "DISABLED", 10 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 11 | "CodecProfile": "MAIN_MAIN", 12 | "QvbrSettings": { 13 | "QvbrQualityLevel": 8 14 | }, 15 | "MaxBitrate": 20000000, 16 | "RateControlMode": "QVBR", 17 | "GopBReference": "ENABLED", 18 | "GopSizeUnits": "SECONDS", 19 | "FlickerAdaptiveQuantization": "ENABLED", 20 | "Telecine": "NONE", 21 | "ParDenominator": 1, 22 | "AdaptiveQuantization": "HIGH", 23 | "TemporalIds": "DISABLED", 24 | "InterlaceMode": "PROGRESSIVE", 25 | "QualityTuningLevel": "SINGLE_PASS_HQ", 26 | "HrdBufferInitialFillPercentage": 90, 27 | "SlowPal": "DISABLED", 28 | "NumberBFramesBetweenReferenceFrames": 3, 29 | "SampleAdaptiveOffsetFilterMode": "ADAPTIVE", 30 | "GopSize": 2.0, 31 | "ParControl": "SPECIFIED", 32 | "Slices": 4, 33 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 34 | "AlternateTransferFunctionSei": "DISABLED", 35 | "ParNumerator": 1, 36 | "SceneChangeDetect": "ENABLED", 37 | "GopClosedCadence": 1, 38 | "HrdBufferSize": 16000000 39 | }, 40 | "Codec": "H_265" 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 2160, 47 | "Width": 3840, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "LC", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 160000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "Mp4Settings": { 74 | "MoovPlacement": "PROGRESSIVE_DOWNLOAD", 75 | "CslgAtom": "INCLUDE", 76 | "FreeSpaceBox": "EXCLUDE" 77 | }, 78 | "Container": "MP4" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /source/custom-resource/lib/metrics/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const axios = require('axios'); 15 | const expect = require('chai').expect; 16 | const MockAdapter = require('axios-mock-adapter'); 17 | 18 | const lambda = require('./index.js'); 19 | 20 | const _config = { 21 | SolutionId: 'SO0021', 22 | UUID: '999-999', 23 | ServiceToken: 'lambda-arn', 24 | Resource: 'AnonymizedMetric' 25 | }; 26 | 27 | describe('#SEND METRICS', () => { 28 | it('should return "200" on a send metrics sucess', async () => { 29 | const mock = new MockAdapter(axios); 30 | mock.onPost().reply(200, {}); 31 | 32 | lambda.send(_config, (_err, res) => { 33 | expect(res).to.equal(200); 34 | }); 35 | }); 36 | 37 | it('should return "Network Error" on connection timedout', async () => { 38 | const mock = new MockAdapter(axios); 39 | mock.onPut().networkError(); 40 | 41 | await lambda.send(_config).catch(err => { 42 | expect(err.toString()).to.equal('Error: Request failed with status code 404'); 43 | }); 44 | }); 45 | 46 | it('should remove ServiceToken and Resource from metrics data', () => { 47 | const sanitizedData = lambda.sanitizeData(_config); 48 | expect(sanitizedData.ServiceToken).to.be.undefined; 49 | expect(sanitizedData.Resource).to.be.undefined; 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /deployment/cdk-solution-helper/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance 5 | * with the License. A copy of the License is located at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions 11 | * and limitations under the License. 12 | */ 13 | 14 | // Imports 15 | const fs = require('fs'); 16 | 17 | // Paths 18 | const global_s3_assets = '../global-s3-assets'; 19 | 20 | // For each template in global_s3_assets ... 21 | fs.readdirSync(global_s3_assets).forEach(file => { 22 | // Import and parse template file 23 | const raw_template = fs.readFileSync(`${global_s3_assets}/${file}`); 24 | let template = JSON.parse(raw_template); 25 | 26 | // Clean-up Lambda function code dependencies 27 | const resources = (template.Resources) ? template.Resources : {}; 28 | const lambdaFunctions = Object.keys(resources).filter(function (key) { 29 | return resources[key].Type === 'AWS::Lambda::Function'; 30 | }); 31 | 32 | lambdaFunctions.forEach(function (f) { 33 | const fn = template.Resources[f]; 34 | let prop; 35 | if (fn.Properties.hasOwnProperty('Code')) { 36 | prop = fn.Properties.Code; 37 | } else if (fn.Properties.hasOwnProperty('Content')) { 38 | prop = fn.Properties.Content; 39 | } 40 | 41 | if (prop.hasOwnProperty('S3Bucket')) { 42 | // Set the S3 key reference 43 | let artifactHash = Object.assign(prop.S3Key); 44 | const assetPath = `asset${artifactHash}`; 45 | prop.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${assetPath}`; 46 | 47 | // Set the S3 bucket reference 48 | prop.S3Bucket = { 49 | 'Fn::Sub': '%%BUCKET_NAME%%-${AWS::Region}' 50 | }; 51 | } else { 52 | console.warn(`No S3Bucket Property found for ${JSON.stringify(prop)}`); 53 | } 54 | }); 55 | 56 | // Clean-up parameters section 57 | const parameters = (template.Parameters) ? template.Parameters : {}; 58 | const assetParameters = Object.keys(parameters).filter(function (key) { 59 | return key.includes('AssetParameters'); 60 | }); 61 | assetParameters.forEach(function (a) { 62 | template.Parameters[a] = undefined; 63 | }); 64 | 65 | // Output modified template file 66 | const output_template = JSON.stringify(template, null, 2); 67 | fs.writeFileSync(`${global_s3_assets}/${file}`, output_template); 68 | }); -------------------------------------------------------------------------------- /source/dynamo/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require('aws-sdk-client-mock'); 17 | const { DynamoDBDocumentClient, UpdateCommand } = require('@aws-sdk/lib-dynamodb'); 18 | const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda'); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#DYNAMODB UPDATE::', () => { 23 | const _event = { 24 | guid: 'SUCCESS', 25 | hello: 'from AWS mock' 26 | }; 27 | 28 | process.env.ErrorHandler = 'error_handler'; 29 | 30 | const dynamoDBDocumentClientMock = mockClient(DynamoDBDocumentClient); 31 | const lambdaClientMock = mockClient(LambdaClient); 32 | afterEach(() => { 33 | dynamoDBDocumentClientMock.reset(); 34 | }); 35 | 36 | it('should return "SUCCESS" when db put returns success', async () => { 37 | dynamoDBDocumentClientMock.on(UpdateCommand).resolves(); 38 | 39 | const response = await lambda.handler(_event); 40 | expect(response.guid).to.equal('SUCCESS'); 41 | }); 42 | 43 | it('should return "DB ERROR" when db put fails', async () => { 44 | dynamoDBDocumentClientMock.on(UpdateCommand).rejects('DB ERROR'); 45 | lambdaClientMock.on(InvokeCommand).resolves(); 46 | 47 | await lambda.handler(_event).catch(err => { 48 | expect(err.toString()).to.equal('Error: DB ERROR'); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /source/archive-source/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require("aws-sdk-client-mock"); 17 | const { S3Client, PutObjectTaggingCommand} = require("@aws-sdk/client-s3"); 18 | const { LambdaClient, InvokeCommand } = require("@aws-sdk/client-lambda"); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#SOURCE ARCHIVE::', () => { 23 | const _event = { 24 | guid: '1234', 25 | srcVideo: 'example.mpg', 26 | srcBucket: 'bucket', 27 | archiveSource: 'GLACIER' 28 | 29 | }; 30 | 31 | process.env.ErrorHandler = 'error_handler'; 32 | process.env.AWS_LAMBDA_FUNCTION_NAME = 'Lambda'; 33 | 34 | const s3ClientMock = mockClient(S3Client); 35 | const lambdaClientMock = mockClient(LambdaClient); 36 | 37 | it('should return "SUCCESS" when s3 tag object returns success', async () => { 38 | s3ClientMock.on(PutObjectTaggingCommand).resolves(); 39 | 40 | const response = await lambda.handler(_event); 41 | expect(response.guid).to.equal('1234'); 42 | }); 43 | 44 | it('should return "TAG ERROR" when s3 tag object fails', async () => { 45 | s3ClientMock.on(PutObjectTaggingCommand).rejects('TAG ERROR'); 46 | lambdaClientMock.on(InvokeCommand).resolves(); 47 | 48 | await lambda.handler(_event).catch(err => { 49 | expect(err).to.equal('TAG ERROR'); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /source/sqs-publish/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require('aws-sdk-client-mock'); 17 | const { SQSClient, SendMessageCommand } = require('@aws-sdk/client-sqs'); 18 | const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda'); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#SNS::', () => { 23 | const _event = { 24 | guid: '12345678', 25 | startTime: 'now', 26 | srcVideo: 'video.mp4', 27 | }; 28 | 29 | process.env.ErrorHandler = 'error_handler'; 30 | process.env.SqsQueue = 'https://sqs.amazonaws.com/1234' 31 | 32 | const lambdaClientMock = mockClient(LambdaClient); 33 | const sQSClientMock = mockClient(SQSClient); 34 | 35 | afterEach(() => sQSClientMock.reset()); 36 | 37 | it('should return "success" on send SQS message', async () => { 38 | sQSClientMock.on(SendMessageCommand).resolves(); 39 | 40 | const response = await lambda.handler(_event); 41 | expect(response.srcVideo).to.equal('video.mp4'); 42 | }); 43 | 44 | it('should return "SQS ERROR" when send SQS message fails', async () => { 45 | sQSClientMock.on(SendMessageCommand).rejects('SQS ERROR'); 46 | lambdaClientMock.on(InvokeCommand).resolves(); 47 | 48 | await lambda.handler(_event).catch(err => { 49 | expect(err.toString()).to.equal('Error: SQS ERROR'); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /source/custom-resource/lib/cfn/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const axios = require('axios'); 15 | const expect = require('chai').expect; 16 | const MockAdapter = require('axios-mock-adapter'); 17 | 18 | const lambda = require('./index.js'); 19 | const _event = { 20 | RequestType: 'Create', 21 | ServiceToken: 'arn:aws:lambda', 22 | ResponseURL: 'https://cloudformation', 23 | StackId: 'arn:aws:cloudformation', 24 | RequestId: '63e8ffa2-3059-4607-a450-119d473c73bc', 25 | LogicalResourceId: 'Uuid', 26 | ResourceType: 'Custom::UUID', 27 | ResourceProperties: { 28 | ServiceToken: 'arn:aws:lambda', 29 | Resource: 'abc' 30 | } 31 | }; 32 | 33 | const _context = { 34 | logStreamName: 'cloudwatch' 35 | }; 36 | 37 | const _responseStatus = 'ok'; 38 | const _responseData = { 39 | test: 'testing' 40 | }; 41 | 42 | describe('#CFN RESPONSE::', () => { 43 | it('should return "200" on a send cfn response sucess', async () => { 44 | const mock = new MockAdapter(axios); 45 | mock.onPut().reply(200, {}); 46 | 47 | lambda.send(_event, _context, _responseStatus, _responseData, (err, res) => { 48 | expect(res.status).to.equal(200); 49 | }); 50 | }); 51 | 52 | it('should return "Network Error" on connection timedout', async () => { 53 | const mock = new MockAdapter(axios); 54 | mock.onPut().networkError(); 55 | 56 | await lambda.send(_event, _context, _responseStatus, _responseData).catch(err => { 57 | expect(err.toString()).to.equal('Error: Network Error'); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/720p_avc_aac_16x9.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_720p_Avc_Aac_16x9", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_400Kbps" 14 | }, 15 | { 16 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_600Kbps" 18 | }, 19 | { 20 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1200Kbps" 22 | }, 23 | { 24 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3500Kbps" 26 | }, 27 | { 28 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3500Kbps" 30 | }, 31 | { 32 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5000Kbps" 34 | }, 35 | { 36 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6500Kbps" 38 | } 39 | ], 40 | "OutputGroupSettings": { 41 | "Type": "HLS_GROUP_SETTINGS", 42 | "HlsGroupSettings": { 43 | "ManifestDurationFormat": "INTEGER", 44 | "SegmentLength": 3, 45 | "TimedMetadataId3Period": 10, 46 | "CaptionLanguageSetting": "OMIT", 47 | "TimedMetadataId3Frame": "PRIV", 48 | "CodecSpecification": "RFC_4281", 49 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 50 | "ProgramDateTimePeriod": 600, 51 | "MinSegmentLength": 0, 52 | "DirectoryStructure": "SINGLE_DIRECTORY", 53 | "ProgramDateTime": "EXCLUDE", 54 | "SegmentControl": "SEGMENTED_FILES", 55 | "ManifestCompression": "NONE", 56 | "ClientCache": "ENABLED", 57 | "StreamInfResolution": "INCLUDE" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/720p_avc_aac_16x9_mvod.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_720p_Avc_Aac_16x9_mvod", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | } 39 | ], 40 | "OutputGroupSettings": { 41 | "Type": "HLS_GROUP_SETTINGS", 42 | "HlsGroupSettings": { 43 | "ManifestDurationFormat": "INTEGER", 44 | "SegmentLength": 3, 45 | "TimedMetadataId3Period": 10, 46 | "CaptionLanguageSetting": "OMIT", 47 | "TimedMetadataId3Frame": "PRIV", 48 | "CodecSpecification": "RFC_4281", 49 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 50 | "ProgramDateTimePeriod": 600, 51 | "MinSegmentLength": 0, 52 | "DirectoryStructure": "SINGLE_DIRECTORY", 53 | "ProgramDateTime": "EXCLUDE", 54 | "SegmentControl": "SEGMENTED_FILES", 55 | "ManifestCompression": "NONE", 56 | "ClientCache": "ENABLED", 57 | "StreamInfResolution": "INCLUDE" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/720p_avc_aac_16x9_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_720p_Avc_Aac_16x9_qvbr", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | } 39 | ], 40 | "OutputGroupSettings": { 41 | "Type": "HLS_GROUP_SETTINGS", 42 | "HlsGroupSettings": { 43 | "ManifestDurationFormat": "INTEGER", 44 | "SegmentLength": 3, 45 | "TimedMetadataId3Period": 10, 46 | "CaptionLanguageSetting": "OMIT", 47 | "TimedMetadataId3Frame": "PRIV", 48 | "CodecSpecification": "RFC_4281", 49 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 50 | "ProgramDateTimePeriod": 600, 51 | "MinSegmentLength": 0, 52 | "DirectoryStructure": "SINGLE_DIRECTORY", 53 | "ProgramDateTime": "EXCLUDE", 54 | "SegmentControl": "SEGMENTED_FILES", 55 | "ManifestCompression": "NONE", 56 | "ClientCache": "ENABLED", 57 | "StreamInfResolution": "INCLUDE" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } -------------------------------------------------------------------------------- /source/mediainfo/setup.py: -------------------------------------------------------------------------------- 1 | ###################################################################################################################### 2 | # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 3 | # # 4 | # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # 5 | # with the License. A copy of the License is located at # 6 | # # 7 | # http://www.apache.org/licenses/LICENSE-2.0 # 8 | # # 9 | # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES # 10 | # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # 11 | # and limitations under the License. # 12 | ###################################################################################################################### 13 | 14 | import sys 15 | from setuptools import setup 16 | from setuptools.command.test import test as TestCommand 17 | from setuptools import Command 18 | import subprocess 19 | 20 | def run_bash_command(command): 21 | try: 22 | subprocess.check_call([command], shell=True) 23 | except subprocess.CalledProcessError as error: 24 | print(f'Command failed with exit code: {error.returncode}') 25 | sys.exit(error.returncode) 26 | 27 | class BuildPackageCommand(Command): 28 | description = 'Build lambda package' 29 | user_options = [ 30 | ('zip-path=', None, 'Location where the zip file will be generated') 31 | ] 32 | 33 | def initialize_options(self): 34 | self.zip_path = None 35 | 36 | def finalize_options(self): 37 | assert self.zip_path is not None, 'Invalid zip_path' 38 | 39 | def run(self): 40 | run_bash_command(f'zip -rq9 {self.zip_path} lambda_function.py ./bin/*') 41 | 42 | class UnitTestsCommand(TestCommand): 43 | description = 'Run unit tests' 44 | 45 | def run_tests(self): 46 | run_bash_command('rm -rf ./pytests && mkdir ./pytests') 47 | run_bash_command('cp lambda_function.py ./test*.py ./pytests') 48 | run_bash_command('python3 -m unittest discover -s ./pytests -v') 49 | run_bash_command('rm -rf ./pytests') 50 | 51 | setup( 52 | name='mediainfo-function', 53 | author='AWS Solutions Builder', 54 | description='Mediainfo function of the Video on Demand on AWS solution', 55 | license='Apache-2.0', 56 | url='https://github.com/awslabs/video-on-demand-on-aws/', 57 | cmdclass={ 58 | 'build_pkg': BuildPackageCommand, 59 | 'test': UnitTestsCommand 60 | } 61 | ) 62 | -------------------------------------------------------------------------------- /source/dynamo/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { DynamoDBDocument } = require("@aws-sdk/lib-dynamodb"); 15 | const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); 16 | const error = require('./lib/error.js'); 17 | 18 | exports.handler = async (event) => { 19 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 20 | 21 | const dynamo = DynamoDBDocument.from(new DynamoDBClient({ 22 | region: process.env.AWS_REGION, 23 | customUserAgent: process.env.SOLUTION_IDENTIFIER 24 | })); 25 | 26 | try { 27 | // Remove guid from event data (primary db table key) and iterate over event objects 28 | // to build the update parameters 29 | let guid = event.guid; 30 | delete event.guid; 31 | let expression = ''; 32 | let values = {}; 33 | let i = 0; 34 | 35 | Object.keys(event).forEach((key) => { 36 | i++; 37 | expression += ' ' + key + ' = :' + i + ','; 38 | values[':' + i] = event[key]; 39 | }); 40 | 41 | let params = { 42 | TableName: process.env.DynamoDBTable, 43 | Key: { 44 | guid: guid, 45 | }, 46 | // remove the trailing ',' from the update expression added by the forEach loop 47 | UpdateExpression: 'set ' + expression.slice(0, -1), 48 | ExpressionAttributeValues: values 49 | }; 50 | 51 | console.log(`UPDATE:: ${JSON.stringify(params, null, 2)}`); 52 | await dynamo.update(params); 53 | 54 | // Get updated data and reconst event data to return 55 | event.guid = guid; 56 | } catch (err) { 57 | await error.handler(event, err); 58 | throw err; 59 | } 60 | 61 | return event; 62 | }; 63 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 3500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 7000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 96000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 5000000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 10000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 96000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 6500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 13000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 720, 47 | "Width": 1280, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 96000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 400000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "MEDIUM", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 45.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 800000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 270, 47 | "Width": 480, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 64000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 1200000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "MEDIUM", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 2400000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 360, 47 | "Width": 640, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 96000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 7 15 | }, 16 | "MaxBitrate": 3500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "ENABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 3, 39 | "HrdBufferSize": 7000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 540, 47 | "Width": 960, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "HEV1", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 96000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "HIGH", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "MinIInterval": 0, 11 | "UnregisteredSeiTimecode": "DISABLED", 12 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 13 | "QvbrSettings": { 14 | "QvbrQualityLevel": 8 15 | }, 16 | "MaxBitrate": 8500000, 17 | "RateControlMode": "QVBR", 18 | "GopBReference": "DISABLED", 19 | "EntropyEncoding": "CABAC", 20 | "GopSizeUnits": "FRAMES", 21 | "FlickerAdaptiveQuantization": "ENABLED", 22 | "Telecine": "NONE", 23 | "ParDenominator": 1, 24 | "AdaptiveQuantization": "HIGH", 25 | "InterlaceMode": "PROGRESSIVE", 26 | "QualityTuningLevel": "SINGLE_PASS_HQ", 27 | "HrdBufferInitialFillPercentage": 90, 28 | "RepeatPps": "DISABLED", 29 | "FieldEncoding": "PAFF", 30 | "SlowPal": "DISABLED", 31 | "GopClosedCadence": 1, 32 | "GopSize": 90.0, 33 | "ParControl": "SPECIFIED", 34 | "Slices": 1, 35 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 36 | "ParNumerator": 1, 37 | "SceneChangeDetect": "ENABLED", 38 | "NumberBFramesBetweenReferenceFrames": 1, 39 | "HrdBufferSize": 17000000 40 | } 41 | }, 42 | "DropFrameTimecode": "ENABLED", 43 | "RespondToAfd": "NONE", 44 | "Sharpness": 50, 45 | "AfdSignaling": "NONE", 46 | "Height": 1080, 47 | "Width": 1920, 48 | "ScalingBehavior": "DEFAULT", 49 | "ColorMetadata": "INSERT", 50 | "AntiAlias": "ENABLED", 51 | "TimecodeInsertion": "DISABLED" 52 | }, 53 | "AudioDescriptions": [{ 54 | "CodecSettings": { 55 | "Codec": "AAC", 56 | "AacSettings": { 57 | "CodecProfile": "LC", 58 | "Specification": "MPEG4", 59 | "RateControlMode": "CBR", 60 | "AudioDescriptionBroadcasterMix": "NORMAL", 61 | "SampleRate": 48000, 62 | "Bitrate": 128000, 63 | "CodingMode": "CODING_MODE_2_0", 64 | "RawFormat": "NONE" 65 | } 66 | }, 67 | "AudioSourceName": "Audio Selector 1", 68 | "AudioTypeControl": "FOLLOW_INPUT", 69 | "LanguageCodeControl": "FOLLOW_INPUT", 70 | "AudioType": 0 71 | }], 72 | "ContainerSettings": { 73 | "M3u8Settings": { 74 | "PatInterval": 0, 75 | "PcrControl": "PCR_EVERY_PES_PACKET", 76 | "PrivateMetadataPid": 503, 77 | "VideoPid": 481, 78 | "ProgramNumber": 1, 79 | "PmtPid": 480, 80 | "PmtInterval": 0, 81 | "AudioPids": [ 82 | 482, 83 | 483, 84 | 484, 85 | 485, 86 | 486, 87 | 487, 88 | 488, 89 | 489, 90 | 490, 91 | 491, 92 | 492, 93 | 493, 94 | 494, 95 | 495, 96 | 496, 97 | 497, 98 | 498 99 | ], 100 | "AudioFramesPerPes": 4 101 | }, 102 | "Container": "M3U8" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/1080p_avc_aac_16x9.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_1080p_Avc_Aac_16x9", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_400Kbps" 14 | }, 15 | { 16 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_600Kbps" 18 | }, 19 | { 20 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1200Kbps" 22 | }, 23 | { 24 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3500Kbps" 26 | }, 27 | { 28 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3500Kbps" 30 | }, 31 | { 32 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5000Kbps" 34 | }, 35 | { 36 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6500Kbps" 38 | }, 39 | { 40 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8500Kbps" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/2160p_avc_aac_16x9.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_2160p_Avc_Aac_16x9", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_400Kbps" 14 | }, 15 | { 16 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_600Kbps" 18 | }, 19 | { 20 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1200Kbps" 22 | }, 23 | { 24 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3500Kbps" 26 | }, 27 | { 28 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3500Kbps" 30 | }, 31 | { 32 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5000Kbps" 34 | }, 35 | { 36 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6500Kbps" 38 | }, 39 | { 40 | "Preset": "System-Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8500Kbps" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/1080p_avc_aac_16x9_mvod.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_1080p_Avc_Aac_16x9_mvod", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | }, 39 | { 40 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/1080p_avc_aac_16x9_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_1080p_Avc_Aac_16x9_qvbr", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | }, 39 | { 40 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/2160p_avc_aac_16x9_mvod.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_2160p_Avc_Aac_16x9_mvod", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | }, 39 | { 40 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/templates/2160p_avc_aac_16x9_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Category": "VOD", 3 | "Description": "video on demand on aws", 4 | "Name": "_Ott_2160p_Avc_Aac_16x9_qvbr", 5 | "Settings": { 6 | "AdAvailOffset": 0, 7 | "OutputGroups": [ 8 | { 9 | "Name": "Apple HLS", 10 | "Outputs": [ 11 | { 12 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr", 13 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_480x270p_0.4Mbps_qvbr" 14 | }, 15 | { 16 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr", 17 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr" 18 | }, 19 | { 20 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr", 21 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_1.2Mbps_qvbr" 22 | }, 23 | { 24 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr", 25 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_960x540p_3.5Mbps_qvbr" 26 | }, 27 | { 28 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr", 29 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_3.5Mbps_qvbr" 30 | }, 31 | { 32 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr", 33 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_5.0Mbps_qvbr" 34 | }, 35 | { 36 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr", 37 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1280x720p_6.5Mbps_qvbr" 38 | }, 39 | { 40 | "Preset": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr", 41 | "NameModifier": "_Ott_Hls_Ts_Avc_Aac_16x9_1920x1080p_8.5Mbps_qvbr" 42 | } 43 | ], 44 | "OutputGroupSettings": { 45 | "Type": "HLS_GROUP_SETTINGS", 46 | "HlsGroupSettings": { 47 | "ManifestDurationFormat": "INTEGER", 48 | "SegmentLength": 3, 49 | "TimedMetadataId3Period": 10, 50 | "CaptionLanguageSetting": "OMIT", 51 | "TimedMetadataId3Frame": "PRIV", 52 | "CodecSpecification": "RFC_4281", 53 | "OutputSelection": "MANIFESTS_AND_SEGMENTS", 54 | "ProgramDateTimePeriod": 600, 55 | "MinSegmentLength": 0, 56 | "DirectoryStructure": "SINGLE_DIRECTORY", 57 | "ProgramDateTime": "EXCLUDE", 58 | "SegmentControl": "SEGMENTED_FILES", 59 | "ManifestCompression": "NONE", 60 | "ClientCache": "ENABLED", 61 | "StreamInfResolution": "INCLUDE" 62 | } 63 | } 64 | } 65 | ] 66 | } 67 | } -------------------------------------------------------------------------------- /source/output-validate/lib/test-events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | const CmafMss = { 7 | detail: { 8 | queue: 'arn:aws:mediaconvert:us-east-1::queues/Default', 9 | jobId: '-htprrb', 10 | userMetadata: { 11 | workflow: 'voodoo', 12 | guid: '9ccbbd94-e39c-4d9b-8f89-85fa1fc81fb4' 13 | }, 14 | outputGroupDetails: [ 15 | { 16 | playlistFilePaths: [ 17 | 's3://vod-destination/12345/cmaf/big_bunny.mpd', 18 | 's3://vod-destination/12345/cmaf/big_bunny.m3u8' 19 | ], 20 | type: 'CMAF_GROUP' 21 | }, 22 | { 23 | outputDetails: [{ 24 | outputFilePaths: [ 25 | 's3://vod-destination/12345/mss/big_bunny.ismv' 26 | ] 27 | }], 28 | playlistFilePaths: [ 29 | 's3://vod-destination/12345/mss/big_bunny.ism' 30 | ], 31 | type: 'MS_SMOOTH_GROUP' 32 | } 33 | ] 34 | } 35 | }; 36 | 37 | const HlsDash = { 38 | detail: { 39 | queue: 'arn:aws:mediaconvert:us-east-1::queues/Default', 40 | jobId: '-htprrb', 41 | status: 'COMPLETE', 42 | userMetadata: { 43 | guid: '12345', 44 | workflow: 'vod4' 45 | }, 46 | outputGroupDetails: [ 47 | { 48 | playlistFilePaths: [ 49 | 's3://vod-destination/12345/hls/dude.m3u8' 50 | ], 51 | type: 'HLS_GROUP' 52 | }, 53 | { 54 | playlistFilePaths: [ 55 | 's3://vod-destination/12345/dash/dude.mpd' 56 | ], 57 | type: 'DASH_ISO_GROUP' 58 | } 59 | ] 60 | } 61 | }; 62 | 63 | const Mp4 = { 64 | version: '0', 65 | id: '371bc689-58cf-71f2-07f7-012b4fa59b79', 66 | 'detail-type': 'MediaConvert Job State Change', 67 | source: 'aws.mediaconvert', 68 | account: '111', 69 | time: '2018-10-17T14:46:06Z', 70 | region: 'us-east-1', 71 | resources: [ 72 | 'arn:aws:mediaconvert:us-east-1::jobs-htprrb' 73 | ], 74 | detail: { 75 | queue: 'arn:aws:mediaconvert:us-east-1::queues/Default', 76 | jobId: '-htprrb', 77 | status: 'COMPLETE', 78 | userMetadata: { 79 | guid: '12345', 80 | workflow: 'vod4' 81 | }, 82 | outputGroupDetails: [ 83 | { 84 | outputDetails: [ 85 | { 86 | outputFilePaths: [ 87 | 's3://vod-destination/12345/mp4/dude_3.0Mbps.mp4' 88 | ], 89 | durationInMs: 13471, 90 | videoDetails: { 91 | widthInPx: 1280, 92 | heightInPx: 720 93 | } 94 | } 95 | ], 96 | type: 'FILE_GROUP' 97 | } 98 | ] 99 | } 100 | }; 101 | 102 | module.exports = { 103 | cmafMss: CmafMss, 104 | hlsDash: HlsDash, 105 | mp4: Mp4 106 | }; 107 | -------------------------------------------------------------------------------- /deployment/run-unit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ "$DEBUG" == 'true' ] && set -x 4 | set -e 5 | 6 | prepare_jest_coverage_report() { 7 | local component_name=$1 8 | 9 | if [ ! -d "coverage" ]; then 10 | echo "ValidationError: Missing required directory coverage after running unit tests" 11 | exit 129 12 | fi 13 | 14 | # prepare coverage reports 15 | rm -fr coverage/lcov-report 16 | mkdir -p "$coverage_reports_top_path/jest" 17 | coverage_report_path="$coverage_reports_top_path/jest/$component_name" 18 | rm -fr "$coverage_report_path" 19 | mv coverage "$coverage_report_path" 20 | } 21 | 22 | run_javascript_test() { 23 | local component_path=$1 24 | local component_name=$2 25 | 26 | echo "------------------------------------------------------------------------------" 27 | echo "[Test] Run javascript unit test with coverage for $component_name" 28 | echo "------------------------------------------------------------------------------" 29 | echo "cd $component_path" 30 | cd "$component_path" 31 | 32 | # install dependencies 33 | npm install --silent 34 | # run unit tests 35 | npm test 36 | 37 | # prepare coverage reports 38 | prepare_jest_coverage_report "$component_name" 39 | } 40 | 41 | # Get reference for all important folders 42 | template_dir="$PWD" 43 | source_dir="$template_dir/../source" 44 | coverage_reports_top_path="$source_dir/test/coverage-reports" 45 | 46 | # Test the attached Lambda function 47 | declare -a lambda_packages=( 48 | "cdk" 49 | "custom-resource" 50 | "archive-source" 51 | "dynamo" 52 | "encode" 53 | "error-handler" 54 | "input-validate" 55 | "media-package-assets" 56 | "output-validate" 57 | "profiler" 58 | "sns-notification" 59 | "sqs-publish" 60 | "step-functions" 61 | ) 62 | 63 | for lambda_package in "${lambda_packages[@]}" 64 | do 65 | rm -rf "$source_dir/$lambda_package/coverage" 66 | mkdir "$source_dir/$lambda_package/coverage" 67 | run_javascript_test "$source_dir/$lambda_package" $lambda_package 68 | 69 | # Check the result of the test and exit if a failure is identified 70 | if [ $? -eq 0 ] 71 | then 72 | echo "Test for $lambda_package passed" 73 | else 74 | echo "******************************************************************************" 75 | echo "Lambda test FAILED for $lambda_package" 76 | echo "******************************************************************************" 77 | exit 1 78 | fi 79 | 80 | done 81 | 82 | 83 | echo "------------------------------------------------------------------------------" 84 | echo "[Test] Run python unit test with coverage for mediainfo" 85 | echo "------------------------------------------------------------------------------" 86 | echo "cd $source_dir/mediainfo" 87 | # If you're running these commands on macOS and Python3 has been installed using Homebrew, you might see this issue: 88 | # DistutilsOptionError: must supply either home or prefix/exec-prefix 89 | # Please follow the workaround suggested on this StackOverflow answer: https://stackoverflow.com/a/44728772 90 | cd ../mediainfo 91 | pip3 install boto3 -t ./pytests --quiet 92 | pip3 install pytest --quiet 93 | pip3 install pytest-cov --quiet 94 | 95 | rm -rf coverage 96 | pytest --cov=. --cov-report xml:coverage/coverage.xml 97 | # fix source file path 98 | sed -i -- 's/filename\=\"/filename\=\"source\/mediainfo\//g' coverage/coverage.xml 99 | mkdir -p "$coverage_reports_top_path/pytest" 100 | mv coverage "$coverage_reports_top_path/pytest/mediainfo" 101 | rm -rf pytests 102 | -------------------------------------------------------------------------------- /source/custom-resource/lib/s3/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require("aws-sdk-client-mock"); 17 | const { S3Client, PutBucketNotificationConfigurationCommand} = require("@aws-sdk/client-s3"); 18 | const { LambdaClient, InvokeCommand } = require("@aws-sdk/client-lambda"); 19 | 20 | const lambda = require('./index.js'); 21 | 22 | const _video = { 23 | WorkflowTrigger: 'VideoFile', 24 | IngestArn: 'arn', 25 | Source: 'srcBucket' 26 | }; 27 | 28 | const _metadata = { 29 | WorkflowTrigger: 'MetadataFile', 30 | IngestArn: 'arn', 31 | Source: 'srcBucket' 32 | }; 33 | 34 | describe('#S3 PUT NOTIFICATION::', () => { 35 | const s3ClientMock = mockClient(S3Client); 36 | const lambdaClientMock = mockClient(LambdaClient); 37 | 38 | it('should succeed on VideoFile trigger', async () => { 39 | s3ClientMock.on(PutBucketNotificationConfigurationCommand).resolves(); 40 | 41 | const response = await lambda.putNotification(_video); 42 | expect(response).to.equal('success'); 43 | }); 44 | 45 | it('should succeed on MetadataFile trigger', async () => { 46 | s3ClientMock.on(PutBucketNotificationConfigurationCommand).resolves(); 47 | 48 | const response = await lambda.putNotification(_metadata); 49 | expect(response).to.equal('success'); 50 | }); 51 | 52 | it('should throw an exception when putBucketNotificationConfiguration fails', async () => { 53 | s3ClientMock.on(PutBucketNotificationConfigurationCommand).rejects('[Error: ERROR]'); 54 | 55 | await lambda.putNotification(_video).catch(err => { 56 | expect(err.toString()).to.equal('Error: [Error: ERROR]'); 57 | }); 58 | }); 59 | 60 | it('should throw an exception when trigger is unknown', async () => { 61 | s3ClientMock.on(PutBucketNotificationConfigurationCommand).resolves(); 62 | const invalid = { WorkflowTrigger: 'bogus' }; 63 | 64 | await lambda.putNotification(invalid).catch(err => { 65 | expect(err.message).to.equal('Unknown WorkflowTrigger: bogus'); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /source/custom-resource/lib/mediaconvert/presets/_Ott_Hls_Ts_Avc_Aac_16x9_640x360p_0.6Mbps_qvbr.json: -------------------------------------------------------------------------------- 1 | { 2 | "VideoDescription": { 3 | "CodecSettings": { 4 | "Codec": "H_264", 5 | "H264Settings": { 6 | "CodecProfile": "MAIN", 7 | "SpatialAdaptiveQuantization": "ENABLED", 8 | "TemporalAdaptiveQuantization": "ENABLED", 9 | "Syntax": "DEFAULT", 10 | "FramerateNumerator": 30000, 11 | "MinIInterval": 0, 12 | "UnregisteredSeiTimecode": "DISABLED", 13 | "FramerateControl": "INITIALIZE_FROM_SOURCE", 14 | "NumberReferenceFrames": 3, 15 | "QvbrSettings": { 16 | "QvbrQualityLevel": 7 17 | }, 18 | "MaxBitrate": 600000, 19 | "RateControlMode": "QVBR", 20 | "GopBReference": "ENABLED", 21 | "EntropyEncoding": "CABAC", 22 | "GopSizeUnits": "FRAMES", 23 | "FlickerAdaptiveQuantization": "ENABLED", 24 | "Telecine": "NONE", 25 | "ParDenominator": 1, 26 | "AdaptiveQuantization": "MEDIUM", 27 | "InterlaceMode": "PROGRESSIVE", 28 | "QualityTuningLevel": "SINGLE_PASS_HQ", 29 | "HrdBufferInitialFillPercentage": 90, 30 | "RepeatPps": "DISABLED", 31 | "FieldEncoding": "PAFF", 32 | "SlowPal": "DISABLED", 33 | "GopClosedCadence": 1, 34 | "GopSize": 90.0, 35 | "ParControl": "SPECIFIED", 36 | "Slices": 1, 37 | "FramerateConversionAlgorithm": "DUPLICATE_DROP", 38 | "ParNumerator": 1, 39 | "SceneChangeDetect": "ENABLED", 40 | "NumberBFramesBetweenReferenceFrames": 3, 41 | "HrdBufferSize": 1200000 42 | } 43 | }, 44 | "DropFrameTimecode": "ENABLED", 45 | "VideoPreprocessors": { 46 | "Deinterlacer": { 47 | "Control": "NORMAL", 48 | "Mode": "DEINTERLACE", 49 | "Algorithm": "INTERPOLATE" 50 | } 51 | }, 52 | "RespondToAfd": "NONE", 53 | "Sharpness": 50, 54 | "AfdSignaling": "NONE", 55 | "Height": 360, 56 | "Width": 640, 57 | "ScalingBehavior": "DEFAULT", 58 | "ColorMetadata": "INSERT", 59 | "AntiAlias": "ENABLED", 60 | "TimecodeInsertion": "DISABLED" 61 | }, 62 | "AudioDescriptions": [{ 63 | "CodecSettings": { 64 | "Codec": "AAC", 65 | "AacSettings": { 66 | "CodecProfile": "HEV1", 67 | "Specification": "MPEG4", 68 | "RateControlMode": "CBR", 69 | "AudioDescriptionBroadcasterMix": "NORMAL", 70 | "SampleRate": 48000, 71 | "Bitrate": 64000, 72 | "CodingMode": "CODING_MODE_2_0", 73 | "RawFormat": "NONE" 74 | } 75 | }, 76 | "AudioSourceName": "Audio Selector 1", 77 | "AudioTypeControl": "FOLLOW_INPUT", 78 | "LanguageCodeControl": "FOLLOW_INPUT", 79 | "AudioType": 0 80 | }], 81 | "ContainerSettings": { 82 | "M3u8Settings": { 83 | "PatInterval": 0, 84 | "PcrControl": "PCR_EVERY_PES_PACKET", 85 | "PrivateMetadataPid": 503, 86 | "VideoPid": 481, 87 | "ProgramNumber": 1, 88 | "PmtPid": 480, 89 | "PmtInterval": 0, 90 | "AudioPids": [ 91 | 482, 92 | 483, 93 | 484, 94 | 485, 95 | 486, 96 | 487, 97 | 488, 98 | 489, 99 | 490, 100 | 491, 101 | 492, 102 | 493, 103 | 494, 104 | 495, 105 | 496, 106 | 497, 107 | 498 108 | ], 109 | "AudioFramesPerPes": 4 110 | }, 111 | "Container": "M3U8" 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 3 | documentation, we greatly value feedback and contributions from our community. 4 | 5 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 6 | information to effectively respond to your bug report or contribution. 7 | 8 | ## Reporting Bugs/Feature Requests 9 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 10 | 11 | When filing an issue, please check [existing open](https://github.com/awslabs/video-on-demand-on-aws/issues), or [recently closed](https://github.com/awslabs/video-on-demand-on-aws/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 12 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 13 | 14 | * A reproducible test case or series of steps 15 | * The version of our code being used 16 | * Any modifications you've made relevant to the bug 17 | * Anything unusual about your environment or deployment 18 | 19 | ## Contributing via Pull Requests 20 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 21 | 22 | 1. You are working against the latest source on the *main* branch. 23 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 24 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 25 | 26 | To send us a pull request, please: 27 | 28 | 1. Fork the repository. 29 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 30 | 3. Ensure local tests pass. 31 | 4. Commit to your fork using clear commit messages. 32 | 5. Send us a pull request, answering any default questions in the pull request interface. 33 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 34 | 35 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 36 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 37 | 38 | ## Finding contributions to work on 39 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/video-on-demand-on-aws/labels/help%20wanted) issues is a great place to start. 40 | 41 | ## Code of Conduct 42 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 43 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 44 | opensource-codeofconduct@amazon.com with any additional questions or comments. 45 | 46 | ## Security issue notifications 47 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 48 | 49 | ## Licensing 50 | 51 | See the [LICENSE](https://github.com/awslabs/video-on-demand-on-aws/blob/main/LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 52 | 53 | We may ask you to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 54 | -------------------------------------------------------------------------------- /source/sns-notification/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { SNS } = require("@aws-sdk/client-sns"); 15 | const error = require('./lib/error.js'); 16 | 17 | const NOT_APPLICABLE_PROPERTIES = [ 18 | 'mp4Outputs', 19 | 'mp4Urls', 20 | 'hlsPlaylist', 21 | 'hlsUrl', 22 | 'dashPlaylist', 23 | 'dashUrl', 24 | 'mssPlaylist', 25 | 'mssUrl', 26 | 'cmafDashPlaylist', 27 | 'cmafDashUrl', 28 | 'cmafHlsPlaylist', 29 | 'cmafHlsUrl' 30 | ]; 31 | 32 | exports.handler = async (event) => { 33 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 34 | 35 | const sns = new SNS({ 36 | region: process.env.AWS_REGION, 37 | customUserAgent: process.env.SOLUTION_IDENTIFIER 38 | }); 39 | 40 | let msg = {}; 41 | 42 | try { 43 | let subject = 'Workflow Status:: ' + event.workflowStatus + ':: ' + event.guid; 44 | 45 | if (event.workflowStatus === 'Complete') { 46 | msg = event; 47 | delete msg.srcMediainfo; 48 | delete msg.jobTemplate_2160p; 49 | delete msg.jobTemplate_1080p; 50 | delete msg.jobTemplate_720p; 51 | delete msg.encodingJob; 52 | delete msg.encodingOutput; 53 | 54 | if (event.enableMediaPackage) { 55 | /* 56 | If MediaPackage VOD is enabled, some properties can be removed from the final output. 57 | They're still saved in DynamoDB, but are not included in the notification. 58 | */ 59 | NOT_APPLICABLE_PROPERTIES.forEach(prop => delete msg[prop]); 60 | } 61 | } else if (event.workflowStatus === 'Ingest') { 62 | msg = { 63 | status: event.workflowStatus, 64 | guid: event.guid, 65 | srcVideo: event.srcVideo 66 | }; 67 | } else { 68 | throw new Error('Workflow Status not defined.'); 69 | } 70 | 71 | console.log(`SEND SNS:: ${JSON.stringify(event, null, 2)}`); 72 | 73 | let params = { 74 | Message: JSON.stringify(msg, null, 2), 75 | Subject: subject, 76 | TargetArn: process.env.SnsTopic 77 | }; 78 | 79 | await sns.publish(params); 80 | } catch (err) { 81 | await error.handler(event, err); 82 | throw err; 83 | } 84 | 85 | return event; 86 | }; 87 | -------------------------------------------------------------------------------- /source/profiler/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require('aws-sdk-client-mock'); 17 | const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb'); 18 | const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda'); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#PROFILER::', () => { 23 | process.env.ErrorHandler = 'error_handler'; 24 | 25 | const _event = { 26 | guid: '12345678' 27 | }; 28 | 29 | const _tmpl_event = { 30 | guid: '12345678', 31 | jobTemplate: 'customTemplate' 32 | }; 33 | 34 | const data = { 35 | Item: { 36 | guid: '12345678', 37 | srcMediainfo: '{ "video": [{ "height": 720, "width": 1280 }] }', 38 | jobTemplate_2160p: 'tmpl1', 39 | jobTemplate_1080p: 'tmpl2', 40 | jobTemplate_720p: 'tmpl3', 41 | frameCapture: true 42 | } 43 | }; 44 | const dynamoDBDocumentClientMock = mockClient(DynamoDBDocumentClient); 45 | const lambdaClientMock = mockClient(LambdaClient); 46 | 47 | afterEach(() => dynamoDBDocumentClientMock.reset()); 48 | 49 | it('should return "SUCCESS" on profile set', async () => { 50 | dynamoDBDocumentClientMock.on(GetCommand).resolves(data); 51 | 52 | const response = await lambda.handler(_event); 53 | expect(response.jobTemplate).to.equal('tmpl3'); 54 | expect(response.frameCaptureHeight).to.equal(720); 55 | expect(response.frameCaptureWidth).to.equal(1280); 56 | expect(response.isCustomTemplate).to.be.false; 57 | }); 58 | 59 | it('should return "SUCCESS" using a custom template', async () => { 60 | dynamoDBDocumentClientMock.on(GetCommand).resolves(data); 61 | 62 | const response = await lambda.handler(_tmpl_event); 63 | expect(response.jobTemplate).to.equal('customTemplate'); 64 | expect(response.frameCaptureHeight).to.equal(720); 65 | expect(response.frameCaptureWidth).to.equal(1280); 66 | expect(response.isCustomTemplate).to.be.true; 67 | }); 68 | 69 | it('should return "DB ERROR" when db get fails', async () => { 70 | dynamoDBDocumentClientMock.on(GetCommand).rejects('DB ERROR'); 71 | lambdaClientMock.on(InvokeCommand).resolves(); 72 | 73 | await lambda.handler(_event).catch(err => { 74 | expect(err.toString()).to.equal('Error: DB ERROR'); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /source/media-package-assets/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { MediaPackageVod } = require("@aws-sdk/client-mediapackage-vod"); 15 | const crypto = require('crypto'); 16 | const error = require('./lib/error'); 17 | 18 | const buildArnFromUri = (s3Uri) => { 19 | const S3_URI_ID = 's3://'; 20 | 21 | if (!s3Uri.startsWith(S3_URI_ID)) { 22 | throw new Error(`Unexpected S3Uri: ${s3Uri}`); 23 | } 24 | 25 | const source = s3Uri.replace(S3_URI_ID, ''); 26 | return `arn:aws:s3:::${source}`; 27 | }; 28 | 29 | /** 30 | * Converts the assets to be advertised behind CloudFront (instead of going directly to MediaPackage). For instance: 31 | * https://endpoint-id.egress.mediapackage-vod.us-east-1.amazonaws.com/out/v1/asset-id/index.m3u8 32 | * => 33 | * https://ditribution-id.cloudfront.net/out/v1/asset-id/index.m3u8 34 | */ 35 | const convertEndpoints = (egressEndpoints, cloudFrontEndpoint) => { 36 | const url = new URL(process.env.GroupDomainName); 37 | let updatedEndpoints = {}; 38 | 39 | egressEndpoints.forEach(endpoint => { 40 | const parts = endpoint.PackagingConfigurationId.split('-'); 41 | const config = parts.pop().toUpperCase(); 42 | 43 | updatedEndpoints[config] = endpoint.Url.replace(url.hostname, cloudFrontEndpoint); 44 | }); 45 | 46 | return updatedEndpoints; 47 | }; 48 | 49 | const handler = async (event) => { 50 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 51 | 52 | const mediaPackageVod = new MediaPackageVod({customUserAgent: process.env.SOLUTION_IDENTIFIER}); 53 | const randomId = crypto.randomBytes(16).toString('hex').toLowerCase(); 54 | 55 | try { 56 | const params = { 57 | Id: randomId, 58 | PackagingGroupId: process.env.GroupId, 59 | SourceArn: buildArnFromUri(event.hlsPlaylist), 60 | SourceRoleArn: process.env.MediaPackageVodRole, 61 | ResourceId: randomId 62 | }; 63 | params.Tags = {'SolutionId': 'SO0021'}; 64 | 65 | console.log(`Ingesting asset:: ${JSON.stringify(params, null, 2)}`); 66 | const response = await mediaPackageVod.createAsset(params); 67 | event.mediaPackageResourceId = randomId; 68 | event.egressEndpoints = convertEndpoints(response.EgressEndpoints, event.cloudFront); 69 | console.log(`ENDPOINTS:: ${JSON.stringify(event.egressEndpoints, null, 2)}`); 70 | } catch (err) { 71 | await error.handler(event, err); 72 | throw err; 73 | } 74 | 75 | return event; 76 | }; 77 | 78 | module.exports = { 79 | handler, 80 | buildArnFromUri 81 | }; 82 | -------------------------------------------------------------------------------- /source/error-handler/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require('aws-sdk-client-mock'); 17 | const { DynamoDBDocumentClient, UpdateCommand } = require('@aws-sdk/lib-dynamodb'); 18 | const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns'); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#ERROR HANDLER::', () => { 23 | const _lambda = { 24 | guid: '1234', 25 | error: 'LAMBDA', 26 | function: 'workflow', 27 | }; 28 | 29 | const _encode = { 30 | guid: '12345678', 31 | error: 'MEDIACONVERT', 32 | errorMessage: 'Encoding Error', 33 | detail: { 34 | jobId: '1111111', 35 | userMetadata: { 36 | guid: 'abcdefg' 37 | } 38 | } 39 | }; 40 | 41 | const dynamoDBDocumentClientMock = mockClient(DynamoDBDocumentClient); 42 | const sNSClientMock = mockClient(SNSClient); 43 | 44 | afterEach(() => { 45 | dynamoDBDocumentClientMock.reset(); 46 | sNSClientMock.reset(); 47 | }); 48 | 49 | it('should return "FROM LAMBDA" processing a lambda error', async () => { 50 | dynamoDBDocumentClientMock.on(UpdateCommand).resolves(); 51 | sNSClientMock.on(PublishCommand).resolves(); 52 | 53 | const response = await lambda.handler(_lambda); 54 | expect(response.error).to.equal('LAMBDA'); 55 | }); 56 | 57 | it('should return "FROM MEDIACONVERT" processing a mediaconvert error', async () => { 58 | dynamoDBDocumentClientMock.on(UpdateCommand).resolves(); 59 | sNSClientMock.on(PublishCommand).resolves(); 60 | 61 | const response = await lambda.handler(_encode); 62 | expect(response.error).to.equal('MEDIACONVERT'); 63 | }); 64 | 65 | it('should return "DB_ERROR" processing a lambda error', async () => { 66 | dynamoDBDocumentClientMock.on(UpdateCommand).rejects('DB_ERROR'); 67 | sNSClientMock.on(PublishCommand).resolves(); 68 | 69 | await lambda.handler(_lambda).catch(err => { 70 | expect(err.toString()).to.equal('Error: DB_ERROR'); 71 | }); 72 | }); 73 | 74 | it('should return "SNS_ERROR" processing a lambda error', async () => { 75 | dynamoDBDocumentClientMock.on(UpdateCommand).resolves(); 76 | sNSClientMock.on(PublishCommand).rejects('SNS_ERROR'); 77 | 78 | await lambda.handler(_encode).catch(err => { 79 | expect(err.toString()).to.equal('Error: SNS_ERROR'); 80 | }); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /source/step-functions/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { SFN: StepFunctions } = require("@aws-sdk/client-sfn"); 15 | const { v4: uuidv4 } = require('uuid'); 16 | const error = require('./lib/error.js'); 17 | 18 | exports.handler = async (event) => { 19 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 20 | 21 | const stepfunctions = new StepFunctions({ 22 | region: process.env.AWS_REGION, 23 | customUserAgent: process.env.SOLUTION_IDENTIFIER 24 | }); 25 | 26 | let response; 27 | let params; 28 | 29 | try { 30 | switch (true) { 31 | case event.hasOwnProperty('Records'): 32 | // Ingest workflow triggered by s3 event:: 33 | event.guid = uuidv4(); 34 | 35 | // Identify file extention of s3 object:: 36 | let key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")); 37 | if (key.slice((key.lastIndexOf(".") - 1 >>> 0) + 2) === 'json') { 38 | event.workflowTrigger = 'Metadata'; 39 | } else { 40 | event.workflowTrigger = 'Video'; 41 | } 42 | params = { 43 | stateMachineArn: process.env.IngestWorkflow, 44 | input: JSON.stringify(event), 45 | name: event.guid 46 | }; 47 | response = 'success'; 48 | break; 49 | 50 | case event.hasOwnProperty('guid'): 51 | // Process Workflow trigger 52 | params = { 53 | stateMachineArn: process.env.ProcessWorkflow, 54 | input: JSON.stringify({ 55 | guid: event.guid 56 | }), 57 | name: event.guid 58 | }; 59 | response = 'success'; 60 | break; 61 | 62 | case event.hasOwnProperty('detail'): 63 | // Publish workflow triggered by MediaConver CloudWatch event:: 64 | params = { 65 | stateMachineArn: process.env.PublishWorkflow, 66 | input: JSON.stringify(event), 67 | name: event.detail.userMetadata.guid 68 | }; 69 | response = 'success'; 70 | break; 71 | 72 | default: 73 | throw new Error('invalid event object'); 74 | } 75 | 76 | let data = await stepfunctions.startExecution(params); 77 | console.log(`STATEMACHINE EXECUTE:: ${JSON.stringify(data, null, 2)}`); 78 | } catch (err) { 79 | await error.handler(event, err); 80 | throw err; 81 | } 82 | 83 | return response; 84 | }; 85 | -------------------------------------------------------------------------------- /source/custom-resource/lib/s3/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { S3 } = require("@aws-sdk/client-s3"); 15 | 16 | /** 17 | * 18 | * Helper function to create S3 ObjectCreated event handler 19 | * 20 | * @param {string} lambdaArn - ARN of the Lambda function to be invoked 21 | * @param {string} suffix - Object suffix to filter event 22 | * @returns 23 | */ 24 | const getS3ObjectCreatedHandlerConfig = (lambdaArn, suffix) => ({ 25 | Events: ['s3:ObjectCreated:*'], 26 | LambdaFunctionArn: lambdaArn, 27 | Filter: { 28 | Key: { 29 | FilterRules: [{ 30 | Name: 'suffix', 31 | Value: suffix 32 | }] 33 | } 34 | } 35 | }) 36 | 37 | /** 38 | * Add event notifications to the source S3 bucket 39 | */ 40 | let PutNotification = async (config) => { 41 | const s3 = new S3({customUserAgent: process.env.SOLUTION_IDENTIFIER}); 42 | 43 | let params; 44 | 45 | switch (config.WorkflowTrigger) { 46 | case 'VideoFile': 47 | const suffixList = [ 48 | '.mpg', 49 | '.mp4', 50 | '.m4v', 51 | '.mov', 52 | '.m2ts', 53 | '.wmv', 54 | '.mxf', 55 | '.mkv', 56 | '.m3u8', 57 | '.mpeg', 58 | '.webm', 59 | '.h264' 60 | ]; 61 | 62 | params = { 63 | Bucket: config.Source, 64 | NotificationConfiguration: { 65 | LambdaFunctionConfigurations: suffixList.map(suffix => ([ 66 | getS3ObjectCreatedHandlerConfig(config.IngestArn, suffix), 67 | getS3ObjectCreatedHandlerConfig(config.IngestArn, suffix.toUpperCase()) 68 | ])).flat() 69 | } 70 | }; 71 | 72 | console.log(`Configuring S3 event for ${config.WorkflowTrigger}`); 73 | await s3.putBucketNotificationConfiguration(params); 74 | break; 75 | 76 | case 'MetadataFile': 77 | params = { 78 | Bucket: config.Source, 79 | NotificationConfiguration: { 80 | LambdaFunctionConfigurations: [ getS3ObjectCreatedHandlerConfig(config.IngestArn, 'json') ] 81 | } 82 | }; 83 | 84 | console.log(`Configuring S3 event for ${config.WorkflowTrigger}`); 85 | await s3.putBucketNotificationConfiguration(params); 86 | break; 87 | 88 | default: 89 | throw new Error(`Unknown WorkflowTrigger: ${config.WorkflowTrigger}`); 90 | } 91 | 92 | return 'success'; 93 | }; 94 | 95 | module.exports = { 96 | putNotification: PutNotification 97 | }; 98 | -------------------------------------------------------------------------------- /source/profiler/index.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const { DynamoDBDocument } = require("@aws-sdk/lib-dynamodb"); 15 | const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); 16 | const error = require('./lib/error.js'); 17 | 18 | exports.handler = async (event) => { 19 | console.log(`REQUEST:: ${JSON.stringify(event, null, 2)}`); 20 | 21 | const dynamo = DynamoDBDocument.from(new DynamoDBClient({ 22 | region: process.env.AWS_REGION, 23 | customUserAgent: process.env.SOLUTION_IDENTIFIER 24 | })); 25 | 26 | try { 27 | // Download DynamoDB data for the source file: 28 | let params = { 29 | TableName: process.env.DynamoDBTable, 30 | Key: { 31 | guid: event.guid 32 | } 33 | }; 34 | 35 | let data = await dynamo.get(params); 36 | 37 | Object.keys(data.Item).forEach(key => { 38 | event[key] = data.Item[key]; 39 | }); 40 | 41 | let mediaInfo = JSON.parse(event.srcMediainfo); 42 | event.srcHeight = mediaInfo.video[0].height; 43 | event.srcWidth = mediaInfo.video[0].width; 44 | 45 | // Determine encoding by matching the srcHeight to the nearest profile. 46 | const profiles = [2160, 1080, 720]; 47 | let lastProfile; 48 | let encodeProfile; 49 | 50 | profiles.some(p => { 51 | let profile = Math.abs(event.srcHeight - p); 52 | if (profile > lastProfile) { 53 | return true; 54 | } 55 | 56 | encodeProfile = p; 57 | lastProfile = profile; 58 | }); 59 | 60 | event.encodingProfile = encodeProfile; 61 | 62 | if (event.frameCapture) { 63 | // Match Height x Width with the encoding profile. 64 | const ratios = { 65 | '2160': 3840, 66 | '1080': 1920, 67 | '720': 1280 68 | }; 69 | 70 | event.frameCaptureHeight = encodeProfile; 71 | event.frameCaptureWidth = ratios[encodeProfile]; 72 | } 73 | 74 | // Update:: added support to pass in a custom encoding Template instead of using the 75 | // solution defaults 76 | if (!event.jobTemplate) { 77 | // Match the jobTemplate to the encoding Profile. 78 | const jobTemplates = { 79 | '2160': event.jobTemplate_2160p, 80 | '1080': event.jobTemplate_1080p, 81 | '720': event.jobTemplate_720p 82 | }; 83 | 84 | event.jobTemplate = jobTemplates[encodeProfile]; 85 | console.log(`Chosen template:: ${event.jobTemplate}`); 86 | 87 | event.isCustomTemplate = false; 88 | } else { 89 | event.isCustomTemplate = true; 90 | } 91 | } catch (err) { 92 | await error.handler(event, err); 93 | throw err; 94 | } 95 | 96 | console.log(`RESPONSE:: ${JSON.stringify(event, null, 2)}`); 97 | return event; 98 | }; 99 | -------------------------------------------------------------------------------- /source/step-functions/lib/index.spec.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************************************************* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * 5 | * with the License. A copy of the License is located at * 6 | * * 7 | * http://www.apache.org/licenses/LICENSE-2.0 * 8 | * * 9 | * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * 10 | * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * 11 | * and limitations under the License. * 12 | *********************************************************************************************************************/ 13 | 14 | const expect = require('chai').expect; 15 | const path = require('path'); 16 | const { mockClient } = require('aws-sdk-client-mock'); 17 | const { SFNClient, StartExecutionCommand } = require('@aws-sdk/client-sfn'); 18 | const { LambdaClient, InvokeCommand } = require('@aws-sdk/client-lambda'); 19 | 20 | const lambda = require('../index.js'); 21 | 22 | describe('#STEP FUNCTIONS::', () => { 23 | process.env.IngestWorkflow = 'INGEST'; 24 | process.env.ProcessWorkflow = 'PROCESS'; 25 | process.env.PublishWorkflow = 'PUBLISH'; 26 | process.env.ErrorHandler = 'error_handler'; 27 | 28 | const _ingest = { 29 | Records: [{ 30 | s3: { 31 | object: { 32 | key: 'big_bunny.mp4', 33 | } 34 | } 35 | }] 36 | }; 37 | 38 | const _process = { 39 | guid: '1234' 40 | }; 41 | 42 | const _publish = { 43 | detail: { 44 | userMetadata: { 45 | guid: '1234' 46 | } 47 | } 48 | }; 49 | 50 | const _error = {}; 51 | 52 | const data = { 53 | executionArn: 'arn' 54 | }; 55 | 56 | const lambdaClientMock = mockClient(LambdaClient); 57 | const sFNClientMock = mockClient(SFNClient); 58 | 59 | afterEach(() => sFNClientMock.reset()); 60 | 61 | it('should return "success" on Ingest Execute success', async () => { 62 | sFNClientMock.on(StartExecutionCommand).resolves(data); 63 | 64 | const response = await lambda.handler(_ingest); 65 | expect(response).to.equal('success'); 66 | }); 67 | 68 | it('should return "success" on process Execute success', async () => { 69 | sFNClientMock.on(StartExecutionCommand).resolves(data); 70 | 71 | const response = await lambda.handler(_process); 72 | expect(response).to.equal('success'); 73 | }); 74 | 75 | it('should return "success" on publish Execute success', async () => { 76 | sFNClientMock.on(StartExecutionCommand).resolves(data); 77 | 78 | const response = await lambda.handler(_publish); 79 | expect(response).to.equal('success'); 80 | }); 81 | 82 | it('should return "ERROR" with an invalid event object', async () => { 83 | sFNClientMock.on(StartExecutionCommand).resolves(data); 84 | lambdaClientMock.on(InvokeCommand).resolves(); 85 | 86 | await lambda.handler(_error).catch(err => { 87 | expect(err.toString()).to.equal('Error: invalid event object'); 88 | }); 89 | }); 90 | 91 | it('should return "STEP ERROR" when step execution fails', async () => { 92 | sFNClientMock.on(StartExecutionCommand).rejects('STEP ERROR'); 93 | lambdaClientMock.on(InvokeCommand).resolves(); 94 | 95 | await lambda.handler(_ingest).catch(err => { 96 | expect(err.toString()).to.equal('Error: STEP ERROR'); 97 | }); 98 | }); 99 | }); 100 | --------------------------------------------------------------------------------