├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── backend
├── CREATEIVS.md
├── README.md
├── README_HTTPS.md
├── create_ivs_channel.sh
├── docker_files
│ ├── Dockerfile
│ ├── buildspec.yml
│ ├── health
│ │ └── health-check.html
│ └── src
│ │ ├── package.json
│ │ ├── transwrap.js
│ │ └── transwrap_local.js
├── get_container_domain.sh
├── health-check.html
├── install_ivs_backend.sh
├── json_configs
│ ├── dynamodb_table.json
│ ├── ivs_codebuild_ecr_policy.json
│ ├── ivs_dynamodb_populate.json
│ ├── ivs_ecs_trust_policy.json
│ ├── ivs_lambda_dynamodb_policy.json
│ ├── ivs_lambda_resource_policy.json
│ ├── ivs_lambda_trust_policy.json
│ └── ivs_webrtc_codebuild_trust_policy.json
├── json_models
│ ├── ecs_services_model.json
│ ├── ivs_codebuild_base_policy_model.json
│ ├── ivs_codebuild_log_policy_model.json
│ ├── ivs_codebuild_model.json
│ ├── ivs_codebuild_s3_policy_model.json
│ ├── ivs_codebuild_vpc_policy_model.json
│ ├── ivs_events_rule_model.json
│ ├── ivs_lambda_resource_policy_model.json
│ ├── ivs_ports.txt
│ ├── ivs_task_definition_model.json
│ └── lambda_model.json
├── lambda_function.py
├── package.json
├── temp_files
│ └── .gitignore
├── transwrap_http.js
├── transwrap_https.js
├── transwrap_local _peer.js
├── transwrap_local.js
└── uninstall_ivs_backend.sh
├── doc
├── IVSCopy.png
├── IVSCreateChannel_1.png
├── IVSCreateChannel_2.png
├── app01.png
├── arch.png
├── auth.png
├── auth01.png
├── codearr.png
├── coderemote.png
├── codevideo.png
├── config.png
├── front.png
├── golive.png
├── saveivs.png
├── sslerror.png
└── videoworkflow.png
└── frontend
├── .gitignore
├── BROWSER.md
├── README.md
├── amplify
├── .config
│ └── project-config.json
├── backend
│ ├── api
│ │ ├── APIGatewayAuthStack.json
│ │ └── saveIVSparam
│ │ │ └── cli-inputs.json
│ ├── auth
│ │ └── simplewebstreaming178ae288
│ │ │ └── cli-inputs.json
│ ├── backend-config.json
│ ├── function
│ │ ├── ISSgetServers
│ │ │ ├── ISSgetServers-cloudformation-template.json
│ │ │ ├── amplify.state
│ │ │ ├── custom-policies.json
│ │ │ ├── function-parameters.json
│ │ │ └── src
│ │ │ │ ├── event.json
│ │ │ │ └── index.js
│ │ └── saveIVSparam
│ │ │ ├── amplify.state
│ │ │ ├── custom-policies.json
│ │ │ ├── function-parameters.json
│ │ │ ├── saveIVSparam-cloudformation-template.json
│ │ │ └── src
│ │ │ ├── event.json
│ │ │ └── index.js
│ ├── storage
│ │ ├── ISStaskdnstrack
│ │ │ └── cli-inputs.json
│ │ └── IVSparam
│ │ │ └── cli-inputs.json
│ ├── tags.json
│ └── types
│ │ └── amplify-dependent-resources-ref.d.ts
└── hooks
│ └── README.md
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── components
├── App.css
├── App.js
├── HomePage.js
├── PlayerView.js
├── StreamForm.js
├── UserMedia.js
├── player
│ └── player.js
└── styles
│ ├── HomePage.style.css
│ ├── PlayerView.style.css
│ ├── StreamForm.style.css
│ └── UserMedia.style.css
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /amplify
6 | /frontend/node_modules
7 | /backend/node_modules
8 | /backend/temp_files
9 | /backend/json_configs
10 | /.pnp
11 | .pnp.js
12 | .pem
13 |
14 | # testing
15 | /coverage
16 | /frontend/.vscode
17 | /frontend/js
18 | /frontend/index.html
19 | /UnderDevCustom
20 |
21 | #amplify
22 | #/frontend/amplify*
23 | /frontend/amplify/.config/local-*
24 | /frontend/amplify/\#current-cloud-backend
25 | /frontend/amplify/mock-data
26 | /frontend/amplify/backend/amplify-meta.json
27 | /frontend/amplify/backend/awscloudformation
28 | /frontend/amplify/logs/
29 | /frontend/amplify/cli.json
30 | /frontend/amplify/team-provider-info.json
31 | /frontend/src/aws-video-exports.js
32 | /frontend/src/aws-exports.js
33 | /frontend/amplify/backend/function/ISSgetServers/dist/latest-build.zip
34 | /frontend/amplify/backend/function/saveIVSparam/dist/latest-build.zip
35 | /frontend/amplify/README.md
36 |
37 | #history
38 | .history
39 |
40 |
41 |
42 | # production
43 | /build
44 | /frontend/build
45 |
46 | # misc
47 | .DS_Store
48 | .env.local
49 | .env.development.local
50 | .env.test.local
51 | .env.production.local
52 | *.eslintcache
53 | .eslintcache
54 | ./eslintcache
55 | /.vscode
56 |
57 | npm-debug.log*
58 | yarn-debug.log*
59 | yarn-error.log*
60 | /doc/index.html
61 | /doc/plan.md
62 |
63 | #under dev
64 | /client*
65 | frontend/package-lock.json
66 | backend/package-lock.json
67 | frontend/stats.json
68 | backend/cert.pem
69 | backend/key.pem
70 | backend/lambda.zip
71 | backend/docker_files/docker.zip
72 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 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.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | 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' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | 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.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this
4 | software and associated documentation files (the "Software"), to deal in the Software
5 | without restriction, including without limitation the rights to use, copy, modify,
6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
7 | permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simplifying live streaming contribution
2 |
3 | A reference code and solution to simplify the live streaming by using the browser APIs and webRTC to capture the video.
4 | The solution is based on small, independent, and decoupled blocks to capture cameras and transwrap video into RTMPS, sending to (Amazon Interactive Video Service(https://aws.amazon.com/ivs/)).
5 |
6 | ## Solution Architecture
7 |
8 |
9 |
10 | ## Solution Components
11 | 1. Broadcaster app: This application uses basic browser APIs to capture audio and video media, and a client web socket to send data to the server web socket. It uses AWS Amplify, which offers tools to build scalable full-stack applications.
12 | 2. Amazon IVS: A managed live streaming solution that is quick and easy to set up and is ideal for creating interactive video experiences.
13 | 3. Proxy video transwrap: This application uses Node.js to open an HTTPS server for the WebSocket communication, and FFmpeg to transwrap the stream in RTMPS to Amazon IVS.
14 |
15 | ## This project is intended for education purposes only and not for production usage.
16 | A reminder that this is a reference solution is not for use in a production environment, but is ideal for testing and accelerating your cloud adoption. Please reach your AWS representative, [click or here](https://pages.awscloud.com/Media-and-Entertainment-Contact-Us.html), for more information on architecting a custom solution for large-scale production-grade events.
17 |
18 | This is a serverless application, leveraging [Amazon IVS](https://aws.amazon.com/ivs), [AWS Amplify](https://aws.amazon.com/amplify/), [Amazon API Gateway](https://aws.amazon.com/api-gateway/), [AWS Lambda](https://aws.amazon.com/lambda/), [Amazon DynamoDB](https://aws.amazon.com/dynamodb), [Amazon S3](https://aws.amazon.com/s3/). The web user interface is built using React.js.
19 |
20 |
21 | ## Deployment options:
22 | :warning: **NOTE:** Deploying this demo application in your AWS account will create and consume AWS resources.
23 |
24 | ### :video_camera: [1. To deploy the application API's and run the client app locally](/frontend/README.md)
25 | We will need to deploy the API's, Lambda Functions and Authentication resources, we will use AWS Amplify.
26 |
27 | ### :rocket: [2. Deployment of the Proxy AWS Fargate and web App Publish](/backend/README.md)
28 | This is an **optional step** to provisione a ECS Docker Container to work a remote transwrap proxy, that will translate from WebRTC to RTMPS.
29 |
30 | ## Simplifying live streaming contribution - run locally.
31 | This sample demo app, captures the video and use a proxy in node.js to transwrap the stream to Amazon IVS
32 |
33 | *Detailed Video Workflow:*
34 |
35 |
36 | The server - transwrap-http.js can be local or remote, according to the steps that you will follow.
37 | For improving browser compatibility, the video element was embedded in a canvas element, as HTMLMediaElement.captureStream() has limited support. HTMLCanvasElement.captureStream() compatibility can be observed in the link [captureStream()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream)
38 |
39 | ## Next step
40 | [Deploy the application API's and run the client app locally](/frontend/README.md)
41 |
42 | ## Additional guides in this repository
43 | * [**Backend: Transwraping Amazon ECS container:**](/backend/README.md)Install the remote video transwrap server.
44 | * [**Create a IVS Channel using a shell script**](/backend/CREATEIVS.md)
45 | * [**Customize the web app and compatibility discussions**](frontend/BROWSER.md)
46 |
47 | ## Notice
48 | This project uses FFMPG, http://www.ffmpeg.org, please check lisensing usage.
49 |
50 | ## Security
51 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
52 |
53 | ## License
54 | This library is licensed under the MIT-0 License. See the LICENSE file.
--------------------------------------------------------------------------------
/backend/CREATEIVS.md:
--------------------------------------------------------------------------------
1 | # Simplifying live streaming contribution - Create a Amazon IVS Channel
2 |
3 | ## [Optional] Channel Creation and Application Configuration
4 | We provide a shell script to create your Amazon IVS Channel, you can use [this guide](https://docs.aws.amazon.com/ivs/latest/userguide/getting-started.html) if you prefer to create it using the AWS Console.
5 | Create a Channel on Amazon Interactive Video Service:
6 |
7 | ```sh
8 | cd simple-streaming-webapp/backend
9 | ./create_ivs_channel.sh ivs-webrtc
10 | ```
11 | ## Copy and save the ingestEndpoint, streamKey value and playbackUrl
12 |
13 | ```
14 | Copy EndPoint: rtmps://6f0eda55d6df.global-contribute.live-video.net:443/app/
15 | Copy StreamKey: sk_us-east-1_siFTKMADpmqe_dzUYPKAZjbE1lcrbQdudLAxyzw
16 | Copy playbackUrl: https://8f97718d90a0.us-east-1.playback.live-video.net/api/video/v1/us-east-1.098435415742.channel.cxyzwe.m3u8
17 | ```
18 |
19 | **Save the params and add to your user interface**
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | # Simplifying live streaming contribution - running in Amazon ECS Container
2 |
3 | ## Deploy the backend environment
4 |
5 | The proxy transwapper is a compound of two containers running a NodeJS web server and FFmpeg. The containers are running on AWS Fargate for Amazon ECS. The AWS Fargate is a serverless compute engine for containers that work with Amazon Elastic Container Service (ECS) and Amazon Elastic Kubernetes Service (EKS).
6 |
7 | In Amazon ECS, you have three building blocks to run your containers successfully:
8 |
9 | Task definition: It is like a blueprint for your application. It is where you which docker images to use and the resources that your container will require.
10 |
11 | Task: It is the instantiation of your task definition.
12 |
13 | Service: It launches and maintains a specified number of copies of the task definition in your cluster.
14 |
15 | In our case, we have a service with two tasks, and each has its own public IP to receive the video stream via WebSocket.
16 |
17 | To track each task's public IP, we are using Amazon EventBridge, AWS Lambda, and Amazon DynamoDB.
18 |
19 | Amazon EventBridge: This is a serverless event bus that makes it easier to build event-driven applications at scale using events generated from your applications, integrated Software-as-a-Service (SaaS) applications, and AWS services. In this case, we built a rule in Amazon EventBridge to track our tasks' start and stop status and trigger the AWS Lambda function.
20 |
21 | AWS Lambda: This is a serverless compute service that lets you run code without provisioning or managing servers, creating workload-aware cluster scaling logic, maintaining event integrations, or managing runtimes. In this case, we built an AWS Lambda Function that, when triggered by the Amazon EventBridge, will update the public IP of the tasks in an Amazon DynamoDB table.
22 |
23 | Amazon DynamoDB: This is a key-value and document database that delivers single-digit millisecond performance at any scale. It's a fully managed, multi-region, multi-active, durable database with built-in security, backup and restores, and in-memory caching for internet-scale applications. In this case, we use an Amazon DynamoDB table to stores the metadata of the tasks. The Public IP of the tasks is part of the metadata.
24 |
25 | The backend deployment is built using a bash shell script located under aws-simple-streaming-webapp/backend that runs AWS CLI commands to build the environment.
26 |
27 | ```sh
28 | cd simple-streaming-webapp/backend
29 | ./install_ivs_backend.sh deploy all
30 | ```
31 |
32 | ## HTTPS considerations
33 | The remote ECS container uses a self signed certificate, so you might have to allow your browser to accept the self signed certificate, or add an Amazon CloudFront distribution to handle HTTPS, or add your own valid certificate to the container.
34 |
35 | To discover the external IP address of your transwrap server you can run:
36 |
37 | ```sh
38 | ./get_container_domain.sh
39 | ```
40 |
41 | :warning: **Note:** For test purpose only.
42 | Open it on your web browser add https:// in front of your domain and accept the self signed certificate.
43 |
44 |
45 |
46 |
47 | ## Cleanup - (Optional): Cleanup, removing the provisioned AWS resources.
48 |
49 | For removing the Transwrap proxy server, you can use the bash script uninstall_ivs_backend.
50 |
51 | ```sh
52 | ./uninstall_ivs_backend.sh clean all
53 | ```
54 |
55 | After it completes, you can clean files
56 |
57 | ```sh
58 | ./uninstall_ivs_backend.sh clean files
59 | ```
60 |
61 |
62 |
--------------------------------------------------------------------------------
/backend/README_HTTPS.md:
--------------------------------------------------------------------------------
1 | ## Transwrap container server instructions
2 |
3 | The backend transwrap server (transwrap.js) is a simple socket server that receives a socket connection data in webm and transwrap / proxies the content to RTMP.
4 |
5 | ### General Instructions
6 |
7 | #### 1) Running in a local envirolment: localhost HTTP
8 |
9 | First install the dependencies and then use the file transwrap_local.js with node.
10 | ```
11 | cd backend/
12 | npm install
13 | npm start-test transwrap_local.js
14 | ```
15 |
16 | it should give the following result
17 |
18 | Listening on port: 3004
19 |
20 | #### 2) Running in HTTPS
21 |
22 | The application frontent requires https, and the transwrap.js is built-in https Node.js module.
23 | For running your server, first we need to create or install a SSL certificate.
24 |
25 | The following instructions will generate the key.pem and cert.pem self-sigend certificates for our server.
26 | Note: Self-signed certificates are not recomended for a non-development evirolment. You can user AWS Certificate Manager (link) to provision, manage, and deploy public and private SSL/TLS
27 |
28 | #### Generating certificate for develoment tests locally in HTTPS
29 |
30 | ```
31 | cd backend/
32 | openssl genrsa -out key.pem
33 | openssl req -new -key key.pem -out csr.pem
34 | openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
35 | rm csr.pem
36 | ```
37 |
38 | Running locally in HTTPS
39 |
40 | ```
41 | npm run startDevs
42 | ```
43 |
44 | The certificates has to be generated in the backend folder.
45 |
46 | :warning: **Note:** For test purpose only.
47 | Open it on your web browser type https://127.0.0.1:3004 and accept the self signed certificate.
48 |
49 |
50 |
51 | ## [Return to the frontend deployment](../frontend/README.md)
52 |
53 | To allow localhost on ssl, you can paste this line in the address bar and proceed to allow localhost, instructions for *chrome only*.
54 |
55 | ```chrome://flags/#allow-insecure-localhost```
56 |
57 | :warning: **Note:** For test purpose only. remenber to restore this configuration after you finish your tests.
58 |
59 | -------
60 | Note: This project uses FFMPEG please check lisencing aspects.
61 | FFmpeg is licensed under the GNU Lesser General Public License (LGPL) version 2.1 or later. However, FFmpeg incorporates several optional parts and optimizations that are covered by the GNU General Public License (GPL) version 2 or later. If those parts get used the GPL applies to all of FFmpeg.
--------------------------------------------------------------------------------
/backend/create_ivs_channel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This script creates the IVS Channel and prints the EndPoint + StreamKey to the user
3 |
4 | # Define colors
5 | RED='\033[0;31m'
6 | GREEN='\033[0;32m'
7 | NC='\033[0m'
8 |
9 | # Creates the IVS channel
10 | aws ivs create-channel --name $1 > ./temp_files/ivs_channel_info.txt
11 |
12 | # Saves RTMPS EndPoint
13 | channelEndpoint=$(cat ./temp_files/ivs_channel_info.txt | jq '.channel.ingestEndpoint' | sed -e 's/"//g;s/^/rtmps:\/\//;s/$/:443\/app\//')
14 |
15 | # Saves StreamKey
16 | streamKey=$(cat ./temp_files/ivs_channel_info.txt | jq '.streamKey.value' | sed -e 's/"//g')
17 |
18 | # Saves PlaybackUrl
19 | playbackUrl=$(cat ./temp_files/ivs_channel_info.txt | jq '.channel.playbackUrl' | sed -e 's/"//g')
20 |
21 | echo -e "\nCopy EndPoint: ${GREEN}${channelEndpoint}${NC}\n"
22 | echo -e "Copy StreamKey: ${GREEN}${streamKey}${NC}\n"
23 | echo -e "Copy PaybackUrl: ${GREEN}${playbackUrl}${NC}\n"
24 |
--------------------------------------------------------------------------------
/backend/docker_files/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | # The line below states we will base our new image on the 18.04 Official Ubuntu
3 |
4 | FROM public.ecr.aws/ubuntu/ubuntu:18.04
5 |
6 | # Identify the maintainer of an image
7 | LABEL authors="Marlon P Campos,Osmar Bento da Silva Junior"
8 |
9 | # Installs new repos, ffmpeg, nodejs, npm and run npm to install modules and creates the certificates for the HTTPS transwrapper.js server
10 | RUN apt-get update \
11 | && apt-get install -y software-properties-common \
12 | && add-apt-repository ppa:jonathonf/ffmpeg-4 -y \
13 | && apt-get update \
14 | && apt-get install --no-install-recommends -y ffmpeg curl \
15 | && curl -sL https://deb.nodesource.com/setup_14.x | bash - \
16 | && apt-get install -y nodejs \
17 | && apt-get clean \
18 | && npm init --yes && npm install --save ws express \
19 | && npm install pm2 -g \
20 | && mkdir -p /opt/ivs-simple-webrtc/www \
21 | && cd /opt/ivs-simple-webrtc \
22 | && openssl genrsa -out /opt/ivs-simple-webrtc/key.pem \
23 | && openssl req -new -key /opt/ivs-simple-webrtc/key.pem -out /opt/ivs-simple-webrtc/csr.pem -subj "/C=US" \
24 | && openssl x509 -req -days 9999 -in /opt/ivs-simple-webrtc/csr.pem -signkey /opt/ivs-simple-webrtc/key.pem -out /opt/ivs-simple-webrtc/cert.pem \
25 | && rm /opt/ivs-simple-webrtc/csr.pem
26 |
27 | # Expose port 80
28 | EXPOSE 80
29 | EXPOSE 443
30 |
31 | # Copy transwraper.js to /opt/ivs-simple-webrtc folder
32 | COPY src* /opt/ivs-simple-webrtc/
33 |
34 | # Copy health-check.html to /opt/ivs-simple-webrtc/www/ folder
35 | COPY health* /opt/ivs-simple-webrtc/www/
36 |
37 | # Start webserver
38 | CMD ["pm2-runtime","/opt/ivs-simple-webrtc/transwrap_local.js","-i max"]
--------------------------------------------------------------------------------
/backend/docker_files/buildspec.yml:
--------------------------------------------------------------------------------
1 | version: 0.2
2 |
3 | phases:
4 | pre_build:
5 | commands:
6 | - echo Logging in to Amazon ECR...
7 | - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
8 | build:
9 | commands:
10 | - echo Build started on `date`
11 | - echo Building the Docker image...
12 | - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
13 | - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
14 | post_build:
15 | commands:
16 | - echo Build completed on `date`
17 | - echo Pushing the Docker image...
18 | - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
--------------------------------------------------------------------------------
/backend/docker_files/health/health-check.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello World
6 |
7 |
8 |
--------------------------------------------------------------------------------
/backend/docker_files/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "transwrap-proxy",
3 | "version": "0.2.0",
4 | "description": "video proxy webm to rtmps",
5 | "main": "transwrap.js",
6 | "dependencies": {
7 | "express": "^4.17.1",
8 | "ws": "^7.4.4"
9 | },
10 | "devDependencies": {},
11 | "scripts": {
12 | "start": "node transwrap.js",
13 | "start-test": "node transwrap_local.js"
14 | },
15 | "author": "osmarb marlonpc",
16 | "license": "MIT"
17 | }
18 |
--------------------------------------------------------------------------------
/backend/docker_files/src/transwrap.js:
--------------------------------------------------------------------------------
1 | // transwrap.js https version
2 |
3 | /// import block
4 | const https = require("https");
5 | const fs = require("fs");
6 | const WebSocket = require("ws");
7 | const express = require("express");
8 | const callff = require("child_process");
9 |
10 | /// app socket definition
11 | const app = express();
12 | const port = 443;
13 | const servertype = "HTTPS";
14 | const home = "wwww"; /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
15 |
16 | // https certificates // Be sure to generate those before running
17 | const newCert = "/opt/ivs-simple-webrtc/cert.pem";
18 | const newKey = "/opt/ivs-simple-webrtc/key.pem";
19 | const certicates = {
20 | key: fs.readFileSync(newKey),
21 | cert: fs.readFileSync(newCert),
22 | };
23 |
24 | // wrapper server
25 | const transwraper = https.createServer(certicates, app).listen(port, () => {
26 | console.log(`Listening on port: ${port} ${servertype}`);
27 | });
28 |
29 | // websocket creation
30 | const wsRef = new WebSocket.Server({ server: transwraper });
31 |
32 | app.use((req, res, next) => {
33 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
34 | return next();
35 | });
36 |
37 | app.use(express.static(__dirname + home));
38 |
39 | // Streaming
40 | wsRef.on("connection", (ws, req) => {
41 | console.log("Loop 1");
42 | ws.send("MSG: Connected to server"); /// Send this message to the app frontend
43 | console.log(`Got connection, URL: ${req.url}`);
44 | ws.on("message", (evt) => {
45 | console.log("Event", evt);
46 | ffmpeg.stdin.write(evt);
47 | });
48 |
49 | ws.on("error", (err) => {
50 | console.log("ERROR on websocket", err);
51 | });
52 |
53 | const rtmpURL = req.url.slice(12);
54 | console.log(`URL seted is ${rtmpURL}`);
55 |
56 | const codec = req.url.split("/")[2];
57 | console.log("CODEC", codec);
58 |
59 | if (codec === "h264") {
60 | console.log("No video transcoding");
61 | var ffArr = [
62 | "-i",
63 | "-",
64 | "-vcodec",
65 | "copy",
66 | "-preset",
67 | "veryfast",
68 | "-tune",
69 | "zerolatency",
70 | "-acodec",
71 | "aac",
72 | "-ar",
73 | "44100",
74 | "-b:a",
75 | "128k",
76 | "-f",
77 | "flv",
78 | rtmpURL,
79 | "-reconnect",
80 | "3",
81 | "-reconnect_at_eof",
82 | "1",
83 | "-reconnect_streamed",
84 | "3",
85 | ];
86 | } else {
87 | console.log("Transcoding true");
88 | //ffmpeg -re -stream_loop -1 -i $VIDEO_FILEPATH -r 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -preset veryfast -x264opts "nal-hrd=cbr:no-scenecut" -minrate 3000 -maxrate 3000 -g 60 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmps://$INGEST_ENDPOINT:443/app/$STREAM_KEY
89 |
90 | var ffArr = [
91 | "-fflags",
92 | "+genpts",
93 | "-i",
94 | "-",
95 | "-r",
96 | "30",
97 | "-c:v",
98 | "libx264",
99 | "-pix_fmt",
100 | "yuv420p",
101 | "-profile:v",
102 | "main",
103 | "-preset",
104 | "veryfast",
105 | "-x264opts",
106 | "nal-hrd=cbr:no-scenecut",
107 | "-minrate",
108 | "3000",
109 | "-maxrate",
110 | "3000",
111 | "-g",
112 | "60",
113 | "-c:a",
114 | "aac",
115 | "-b:a",
116 | "128k",
117 | "-ac",
118 | "2",
119 | "-ar",
120 | "44100",
121 | "-vf",
122 | "scale=1280:720,format=yuv420p",
123 | "-profile:v",
124 | "main",
125 | "-f",
126 | "flv",
127 | rtmpURL,
128 | "-reconnect",
129 | "3",
130 | "-reconnect_at_eof",
131 | "1",
132 | "-reconnect_streamed",
133 | "3",
134 | ];
135 | }
136 |
137 | ws.on("close", (evt) => {
138 | ffmpeg.kill("SIGINT");
139 | console.log(`Connection Closed: ${evt}`);
140 | });
141 |
142 | const ffmpeg = callff.spawn("ffmpeg", ffArr);
143 |
144 | ffmpeg.on("close", (code, signal) => {
145 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
146 | ws.send("Closing Socket"); /// message to front end
147 | ws.terminate();
148 | });
149 |
150 | ffmpeg.stdin.on("error", (e) => {
151 | ws.send(e);
152 | console.log(`FFMPEG ERROR: ${e}`);
153 | ws.terminate();
154 | });
155 |
156 | ffmpeg.stderr.on("data", (data) => {
157 | console.log(`FFMPEG MSG: ${data.toString()}`);
158 | ws.send(data.toString());
159 | });
160 | });
161 |
--------------------------------------------------------------------------------
/backend/docker_files/src/transwrap_local.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | // transwrap.js http version
5 |
6 | /// import block
7 | const http = require("http");
8 | //const fs = require('fs');
9 | const WebSocket = require("ws");
10 | const express = require("express");
11 | const callff = require("child_process");
12 |
13 | /// app socket definition
14 | const app = express();
15 | const port = 80;
16 | const servertype = "HTTP";
17 | const home = "/wwww"; /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
18 |
19 | // https certificates // Be sure to generate those before running
20 | /*
21 | const newCert = 'cert.pem';
22 | const newKey = 'key.pem'
23 | const certicates = {
24 | key: fs.readFileSync(newKey),
25 | cert: fs.readFileSync(newCert)
26 | };
27 | */
28 |
29 | // wrapper server
30 | const transwraper = http.createServer(app).listen(port, () => {
31 | console.log(`Listening on port: ${port} ${servertype}`);
32 | });
33 |
34 | // websocket creation
35 | const wsRef = new WebSocket.Server({ server: transwraper });
36 |
37 | app.use((req, res, next) => {
38 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
39 | return next();
40 | });
41 |
42 | app.use(express.static(__dirname + home));
43 |
44 | // Streaming
45 | wsRef.on("connection", (ws, req) => {
46 | console.log("Loop 1");
47 | ws.send("MSG: Connected to server"); /// Send this message to the app frontend
48 | console.log(`Got connection, URL: ${req.url}`);
49 | ws.on("message", (evt) => {
50 | console.log("Event", evt);
51 | ffmpeg.stdin.write(evt);
52 | });
53 |
54 | ws.on("error", (err) => {
55 | console.log("ERROR on websocket", err);
56 | });
57 |
58 | const rtmpURL = req.url.slice(12);
59 | console.log(`URL seted is ${rtmpURL}`);
60 |
61 | const codec = req.url.split("/")[2];
62 | console.log("CODEC", codec);
63 |
64 | if (codec === "h264") {
65 | console.log("No video transcoding");
66 | var ffArr = [
67 | "-i",
68 | "-",
69 | "-vcodec",
70 | "copy",
71 | "-preset",
72 | "veryfast",
73 | "-tune",
74 | "zerolatency",
75 | "-acodec",
76 | "aac",
77 | "-ar",
78 | "44100",
79 | "-b:a",
80 | "128k",
81 | "-f",
82 | "flv",
83 | rtmpURL,
84 | "-reconnect",
85 | "3",
86 | "-reconnect_at_eof",
87 | "1",
88 | "-reconnect_streamed",
89 | "3",
90 | ];
91 | } else {
92 | console.log("Transcoding true");
93 | //ffmpeg -re -stream_loop -1 -i $VIDEO_FILEPATH -r 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -preset veryfast -x264opts "nal-hrd=cbr:no-scenecut" -minrate 3000 -maxrate 3000 -g 60 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmps://$INGEST_ENDPOINT:443/app/$STREAM_KEY
94 |
95 | var ffArr = [
96 | "-fflags",
97 | "+genpts",
98 | "-i",
99 | "-",
100 | "-r",
101 | "30",
102 | "-c:v",
103 | "libx264",
104 | "-pix_fmt",
105 | "yuv420p",
106 | "-profile:v",
107 | "main",
108 | "-preset",
109 | "veryfast",
110 | "-x264opts",
111 | "nal-hrd=cbr:no-scenecut",
112 | "-minrate",
113 | "3000",
114 | "-maxrate",
115 | "3000",
116 | "-g",
117 | "60",
118 | "-c:a",
119 | "aac",
120 | "-b:a",
121 | "128k",
122 | "-ac",
123 | "2",
124 | "-ar",
125 | "44100",
126 | "-vf",
127 | "scale=1280:720,format=yuv420p",
128 | "-profile:v",
129 | "main",
130 | "-f",
131 | "flv",
132 | rtmpURL,
133 | "-reconnect",
134 | "3",
135 | "-reconnect_at_eof",
136 | "1",
137 | "-reconnect_streamed",
138 | "3",
139 | ];
140 | }
141 |
142 | ws.on("close", (evt) => {
143 | ffmpeg.kill("SIGINT");
144 | console.log(`Connection Closed: ${evt}`);
145 | });
146 |
147 | const ffmpeg = callff.spawn("ffmpeg", ffArr);
148 |
149 | ffmpeg.on("close", (code, signal) => {
150 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
151 | ws.send("Closing Socket"); /// message to front end
152 | ws.terminate();
153 | });
154 |
155 | ffmpeg.stdin.on("error", (e) => {
156 | ws.send(e);
157 | console.log(`FFMPEG ERROR: ${e}`);
158 | ws.terminate();
159 | });
160 |
161 | ffmpeg.stderr.on("data", (data) => {
162 | console.log(`FFMPEG MSG: ${data.toString()}`);
163 | ws.send(data.toString());
164 | });
165 | });
166 |
--------------------------------------------------------------------------------
/backend/get_container_domain.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # This script get the domain from the dynamo table and set as a env var for the CloudFront Distribution Creation
3 |
4 | # Define colors
5 | RED='\033[0;31m'
6 | GREEN='\033[0;32m'
7 | NC='\033[0m'
8 |
9 | # Var Definitons
10 |
11 | DyTable='ISS-task-dns-track-dev'
12 | AwsAccProfile='default'
13 | Region='us-east-1'
14 |
15 | # Scan the Dynamo Table
16 | aws dynamodb scan --table-name $DyTable --filter-expression "replaced = :n" --expression-attribute-values {\":n\":{\"S\":\"no\"}} --max-items 1 --profile $AwsAccProfile --region $Region > ./temp_files/dynamo_scan.txt
17 |
18 |
19 | # Saves RTMPS EndPoint
20 | DomainResults=$(cat ./temp_files/dynamo_scan.txt| jq '.Items[].dns.S')
21 |
22 | echo -e "\nCopy Domain: ${GREEN}${DomainResults}${NC}\n"
23 | export DOMAIN=${DomainResults}
--------------------------------------------------------------------------------
/backend/health-check.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello World
6 |
7 |
8 |
--------------------------------------------------------------------------------
/backend/install_ivs_backend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define colors
4 | RED='\033[0;31m'
5 | GREEN='\033[0;32m'
6 | YELLOW='\033[0;93m'
7 | NC='\033[0m'
8 |
9 | # This is a very simples scrip that uses aws cli + jq + sed to deploy the backend of aws-simples-streaming-app. You could easly deploy the whole back end by inserting individual commands
10 | # but it would consume more of your time. The script will allow you to save time during the deployment.
11 |
12 |
13 | ################################################################################
14 | # Help #
15 | ################################################################################
16 |
17 | function help ()
18 | {
19 | # Display Help
20 | echo -e "${GREEN}This script deploys the backend of the aws-simple-streaming app."
21 | echo
22 | echo -e "Syntax: ./install_ivs_backend.sh deploy [ all | ecr | s3 | templates | codebuild_rp | codebuild | iam | lambda | dynamodb | sg | fargate | events | logs | tasks ]${NC}"
23 | echo -e "${YELLOW}options:"
24 | echo -e "all Deploys the whole backend at once"
25 | echo -e "ecr Creates the ecr repository"
26 | echo -e "s3 Creates s3 bucket and upload the docker files"
27 | echo -e "templates Prepares the templates files for codebuild configuration"
28 | echo -e "codebuild_rp Prepares all roles and policies required by codebuild"
29 | echo -e "codebuild Creates the codebuild project and build the container images"
30 | echo -e "iam Prepares all roles and policies required by lambda,dynamodb and fargate"
31 | echo -e "lambda Creates the lambda function"
32 | echo -e "dynamodb Check if the dynamodb table exists and populate it with initial values"
33 | echo -e "sg Creates the segurity groups required by fargate"
34 | echo -e "fargate Configures ECS cluster, task definitions and services"
35 | echo -e "events Configures the eventbridge rules and triggers"
36 | echo -e "tasks Configures ECS service to start 2 tasks${NC}\n\n"
37 |
38 | echo -e "${GREEN}Example on how to use this script to deploy the whole backend at once"
39 | echo -e "./install_ivs_backend.sh deploy all${NC}\n\n"
40 |
41 | echo -e "${YELLOW}Case you prefer to deploy the backend step by step, deploy the each item following the order showing in this help"
42 | echo -e "Example:"
43 | echo -e "./install_ivs_backend.sh deploy ecr"
44 | echo -e "./install_ivs_backend.sh deploy s3"
45 | echo -e "./install_ivs_backend.sh deploy templates"
46 | echo -e "...and so on...${NC}\n\n"
47 |
48 |
49 | }
50 |
51 | ################################################################################
52 | # ECR Resources #
53 | ################################################################################
54 |
55 | function ecr_resources () {
56 |
57 | # This function will create the ECR repository required by ivs-webrtc
58 |
59 | # Test if it exists
60 | aws ecr describe-repositories --repository-names ivs-webrtc > /dev/null 2>&1 && ecr='OK' || ecr='NOK'
61 |
62 | if [ $ecr = 'NOK' ]
63 | then
64 |
65 | echo -e "${GREEN}Creating ECR ivs-webrtc repository...${NC}"
66 | aws ecr create-repository --repository-name ivs-webrtc > ./temp_files/ecr_repository.txt
67 | jq '.repository.registryId' ./temp_files/ecr_repository.txt | sed 's/"//g' > ./temp_files/accountid.txt
68 | jq '.repository.repositoryUri' ./temp_files/ecr_repository.txt | sed 's/"//g' > ./temp_files/ecr_uri.txt
69 |
70 | else
71 |
72 | echo -e "${GREEN}ECR repository already exists. Capturing the registryId...${NC}"
73 | aws ecr describe-repositories --repository-names ivs-webrtc > ./temp_files/ecr_repository.txt
74 | jq '.repositories[0].registryId' ./temp_files/ecr_repository.txt | sed 's/"//g' > ./temp_files/accountid.txt
75 | jq '.repositories[0].repositoryUri' ./temp_files/ecr_repository.txt | sed 's/"//g' > ./temp_files/ecr_uri.txt
76 |
77 | fi
78 |
79 | unset ecr
80 |
81 | }
82 |
83 | ################################################################################
84 | # S3 Resources #
85 | ################################################################################
86 |
87 | function s3_resources () {
88 |
89 | # This function creates a s3 bucket to store the files required by codebuild
90 |
91 | # Creates a token
92 | token=$(uuidgen | sed -n 's/.*-//;p' | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ )
93 |
94 | # Creates a bucket
95 | aws s3 mb s3://ivs-webrtc-codebuild-${token} > /dev/null 2>&1 && s3='OK' || s3='NOK'
96 |
97 | copy_ready='NOK'
98 |
99 | if [ $s3 = 'NOK' ]
100 | then
101 |
102 | # Creates the S3 bucket
103 | echo -e "${GREEN}Creating S3 bucket...${NC}"
104 | token=$(uuidgen | sed -n 's/.*-//;p' | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ )
105 | aws s3 mb s3://ivs-webrtc-codebuild-${token} > /dev/null 2>&1 && s3='OK' || s3='NOK'
106 | echo "ivs-webrtc-codebuild-${token}" > ./temp_files/s3_bucket.txt
107 | copy_ready='OK'
108 |
109 | else
110 |
111 | # Case bucket already exists, it saves its name in ./temp_files/s3_bucket.txt
112 | echo -e "${GREEN}It seems that bucket ivs-webrtc-codebuild-${token} already exists. Capturing the details...${NC}"
113 | echo "ivs-webrtc-codebuild-${token}" > ./temp_files/s3_bucket.txt
114 | copy_ready='OK'
115 |
116 | fi
117 |
118 | unset s3
119 |
120 | if [ $copy_ready = 'OK' ]
121 | then
122 |
123 | # Zip dockerfiles and dependencies and copy them to s3 bucket
124 | echo -e "${GREEN}Generating docker.zip and upload to ivs-webrtc-codebuild-${token} bucket...${NC}"
125 | cd ./docker_files/
126 | echo -e "${GREEN}Compacting files into docker.zip...${NC}"
127 | zip -r docker.zip *
128 | cd ..
129 | echo -e "${GREEN}Uploading docker.zip to ivs-webrtc-codebuild-${token} bucket...${NC}"
130 | aws s3 cp ./docker_files/docker.zip s3://ivs-webrtc-codebuild-${token}
131 | fi
132 |
133 | }
134 |
135 | ################################################################################
136 | # Codebuild Templates #
137 | ################################################################################
138 |
139 | function codebuild_adjust_templates () {
140 |
141 | # This function adjust the json_model templates required by codebuild deployment
142 |
143 | # Capture the subnet that will be used by codebuild project
144 | replace_subnets=($(aws ec2 describe-subnets --filter Name=vpc-id,Values=$(aws ec2 describe-vpcs | jq '.Vpcs[] | select(.IsDefault)' | jq '.VpcId' | sed 's/"//g') --query 'Subnets[?MapPublicIpOnLaunch==`true`].SubnetId' | sed -e 's/\[//g;s/\]//g;s/ //g;s/"//g;s/,//g;1d;$ d' | sed -e :a -e ';$!N;s/\n/ /;ta')) > ./temp_files/codebuild_subnets.txt
145 | sleep 2
146 |
147 | ### BEGIN - EXEMPTION THIS SHOULD BE HERE
148 | # Test if exists
149 | aws iam get-role --role-name ivs-webrtc-codebuild-role > /dev/null 2>&1 && codebuild='OK' || codebuild='NOK'
150 | sleep 2
151 |
152 | if [ $codebuild = 'NOK' ]
153 | then
154 |
155 | # Create the AWS Codebuild role that will be used on our Codebuild Project
156 | echo -e "${GREEN}Creating AWS Codebuild role...${NC}"
157 | aws iam create-role --role-name ivs-webrtc-codebuild-role --assume-role-policy-document file://json_configs/ivs_webrtc_codebuild_trust_policy.json | jq '.Role.Arn' | sed 's/"//g' > ./temp_files/ivs_webrtc_codebuild_role_arn.txt
158 | sleep 2
159 | else
160 |
161 | # If Codebuild role exist already, save its Arn in ./temp_files/ivs_webrtc_codebuild_role_arn.txt
162 | aws iam get-role --role-name ivs-webrtc-codebuild-role | jq '.Role.Arn' | sed 's/"//g' > ./temp_files/ivs_webrtc_codebuild_role_arn.txt
163 | sleep 2
164 | fi
165 |
166 |
167 | unset codebuild
168 | ### BEGIN - EXEMPTION THIS SHOULD BE HERE
169 |
170 | # Variables that will be used to replace the values in json_model templates
171 | replace_accountid=$(cat ./temp_files/accountid.txt)
172 | replace_location=$(cat ./temp_files/s3_bucket.txt)/docker.zip
173 | replace_bucket=$(cat ./temp_files/s3_bucket.txt)
174 | replace_kms="arn:aws:kms:us-east-1:${replace_accountid}:alias/aws/s3"
175 | replace_role=$(cat ./temp_files/ivs_webrtc_codebuild_role_arn.txt)
176 |
177 | # This will generate the ./json_configs/ivs_codebuild.json from the template ./json_models/ivs_codebuild_model.json
178 | jq --arg a "$replace_accountid" --arg l "$replace_location" --arg r "$replace_role" --arg k "$replace_kms" '.environment.environmentVariables[1].value = $a | .source.location = $l | .serviceRole = $r | .encryptionKey = $k ' ./json_models/ivs_codebuild_model.json > ./json_configs/ivs_codebuild.json
179 |
180 | # This will generate the ./json_configs/ivs_codebuild_base_policy.json from the template ./json_models/ivs_codebuild_base_policy_model.json
181 | sed -e "s/REPLACE_ACCOUNTID/$replace_accountid/g" ./json_models/ivs_codebuild_base_policy_model.json > ./json_configs/ivs_codebuild_base_policy.json
182 |
183 | # This will generate the ./json_configs/ivs_codebuild_s3_policy.json from the template ./json_models/ivs_codebuild_s3_policy_model.json
184 | sed -e "s/REPLACE_BUCKET/$replace_bucket/g" ./json_models/ivs_codebuild_s3_policy_model.json > ./json_configs/ivs_codebuild_s3_policy.json
185 |
186 | # This will generate the ./json_configs/ivs_codebuild_log_policy.json from the template ./json_models/ivs_codebuild_log_policy_model.json
187 | sed -e "s/REPLACE_ACCOUNTID/$replace_accountid/g" ./json_models/ivs_codebuild_log_policy_model.json > ./json_configs/ivs_codebuild_log_policy.json
188 |
189 | # This will generate the
190 | sed -e "s/REPLACE_ACCOUNTID/$replace_accountid/g;s/REPLACE_SUBNET0/${replace_subnets[0]}/g;s/REPLACE_SUBNET1/${replace_subnets[1]}/g;s/REPLACE_SUBNET2/${replace_subnets[2]}/g;" ./json_models/ivs_codebuild_vpc_policy_model.json > ./json_configs/ivs_codebuild_vpc_policy.json
191 | }
192 |
193 | ################################################################################
194 | # Codebuild Roles and Policies #
195 | ################################################################################
196 |
197 | function codebuild_iam () {
198 |
199 | # Test if exists
200 | codebuild=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_codebuild_ecr")' | jq '.Arn' | sed 's/"//g')
201 | sleep 2
202 |
203 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
204 | if [ ! $codebuild ]
205 | then
206 |
207 | echo -e "${GREEN}Attaching ivs_codebuild_ecr policy to AWS Codebuild role...${NC}"
208 | aws iam create-policy --policy-name ivs_codebuild_ecr --policy-document file://json_configs/ivs_codebuild_ecr_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/ivs_codebuild_ecr_arn.txt
209 | sleep 2
210 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $(cat ./temp_files/ivs_codebuild_ecr_arn.txt)
211 | sleep 2
212 |
213 | else
214 |
215 | echo -e "${GREEN}Attaching ivs_codebuild_ecr policy to AWS Codebuild role...${NC}"
216 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $codebuild
217 | sleep 2
218 |
219 | fi
220 |
221 | unset codebuild
222 |
223 | # Test if exists
224 | codebuild=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_codebuild_base")' | jq '.Arn' | sed 's/"//g')
225 | sleep 2
226 |
227 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
228 | if [ ! $codebuild ]
229 | then
230 |
231 | echo -e "${GREEN}Attaching ivs_codebuild_base policy to AWS Codebuild role...${NC}"
232 | aws iam create-policy --policy-name ivs_codebuild_base --policy-document file://json_configs/ivs_codebuild_base_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/ivs_codebuild_base_arn.txt
233 | sleep 2
234 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $(cat ./temp_files/ivs_codebuild_base_arn.txt)
235 | sleep 2
236 |
237 | else
238 |
239 | echo -e "${GREEN}Attaching ivs_codebuild_base policy to AWS Codebuild role...${NC}"
240 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $codebuild
241 | sleep 2
242 |
243 | fi
244 |
245 | unset codebuild
246 |
247 | # Test if exists
248 | codebuild=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_codebuild_vpc")' | jq '.Arn' | sed 's/"//g')
249 | sleep 2
250 |
251 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
252 | if [ ! $codebuild ]
253 | then
254 |
255 | echo -e "${GREEN}Attaching ivs_codebuild_vpc policy to AWS Codebuild role...${NC}"
256 | aws iam create-policy --policy-name ivs_codebuild_vpc --policy-document file://json_configs/ivs_codebuild_vpc_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/ivs_codebuild_vpc_arn.txt
257 | sleep 2
258 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $(cat ./temp_files/ivs_codebuild_vpc_arn.txt)
259 | sleep 2
260 |
261 | else
262 |
263 | echo -e "${GREEN}Attaching ivs_codebuild_vpc policy to AWS Codebuild role...${NC}"
264 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $codebuild
265 | sleep 2
266 |
267 | fi
268 |
269 | unset codebuild
270 |
271 | # Test if exists
272 | codebuild=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_codebuild_s3")' | jq '.Arn' | sed 's/"//g')
273 | sleep 2
274 |
275 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
276 | if [ ! $codebuild ]
277 | then
278 |
279 | echo -e "${GREEN}Attaching ivs_codebuild_s3 policy to AWS Codebuild role...${NC}"
280 | aws iam create-policy --policy-name ivs_codebuild_s3 --policy-document file://json_configs/ivs_codebuild_s3_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/ivs_codebuild_s3_arn.txt
281 | sleep 2
282 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $(cat ./temp_files/ivs_codebuild_s3_arn.txt)
283 | sleep 2
284 |
285 | else
286 |
287 | echo -e "${GREEN}Attaching ivs_codebuild_s3 policy to AWS Codebuild role...${NC}"
288 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $codebuild
289 | sleep 2
290 |
291 | fi
292 |
293 | unset codebuild
294 |
295 | # Test if exists
296 | codebuild=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_codebuild_log")' | jq '.Arn' | sed 's/"//g')
297 | sleep 2
298 |
299 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
300 | if [ ! $codebuild ]
301 | then
302 |
303 | echo -e "${GREEN}Attaching ivs_codebuild_log policy to AWS Codebuild role...${NC}"
304 | aws iam create-policy --policy-name ivs_codebuild_log --policy-document file://json_configs/ivs_codebuild_log_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/ivs_codebuild_log_arn.txt
305 | sleep 2
306 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $(cat ./temp_files/ivs_codebuild_log_arn.txt)
307 | sleep 2
308 |
309 | else
310 |
311 | echo -e "${GREEN}Attaching ivs_codebuild_log policy to AWS Codebuild role...${NC}"
312 | aws iam attach-role-policy --role-name ivs-webrtc-codebuild-role --policy-arn $codebuild
313 | sleep 2
314 |
315 | fi
316 |
317 | unset codebuild
318 | }
319 |
320 | ################################################################################
321 | # Progress bar #
322 | ################################################################################
323 |
324 | function print_progress () {
325 |
326 | # Print # on screen
327 |
328 | for i in {0..7}
329 | do
330 | sleep 2
331 | echo -n "#"
332 |
333 | done
334 |
335 | }
336 |
337 |
338 | ################################################################################
339 | # Codebuild Resources #
340 | ################################################################################
341 |
342 | function codebuild_resources () {
343 |
344 | # Create codebuild project
345 |
346 | aws codebuild create-project --name ivs-webrtc --cli-input-json file://json_configs/ivs_codebuild.json > /dev/null 2>&1
347 |
348 | build_current_phase='NOT_STARTED'
349 |
350 | aws codebuild start-build --project-name ivs-webrtc > ./temp_files/ivs_codebuild_build.json
351 | ivs_build=$(jq '.build.id' ./temp_files/ivs_codebuild_build.json | sed 's/"//g')
352 |
353 | echo -e "${GREEN}Creating the container image${NC}"
354 |
355 | print_progress
356 |
357 | while true
358 | do
359 | build_status=$(aws codebuild batch-get-builds --ids $ivs_build | jq '.builds[0].currentPhase' | sed 's/"//g')
360 |
361 | if [ $build_status = 'PROVISIONING' ]
362 | then
363 |
364 | echo -e "${YELLOW}${build_status}${NC}"
365 | print_progress
366 |
367 | elif [ $build_status = 'PRE_BUILD' ]
368 | then
369 |
370 | echo -e "${YELLOW}${build_status}${NC}"
371 | print_progress
372 |
373 | elif [ $build_status = 'BUILD' ]
374 | then
375 |
376 | echo -e "${YELLOW}${build_status}${NC}"
377 | print_progress
378 |
379 | elif [ $build_status = 'POST_BUILD' ]
380 | then
381 |
382 | echo -e "${YELLOW}${build_status}${NC}"
383 | print_progress
384 |
385 | elif [ $build_status = 'FINALIZING' ]
386 | then
387 |
388 | echo -e "${YELLOW}${build_status}${NC}"
389 | print_progress
390 |
391 | elif [ $build_status = 'COMPLETED' ]
392 | then
393 |
394 | phase_status=$(aws codebuild batch-get-builds --ids $ivs_build | jq '.builds[0].currentPhase' | sed 's/"//g')
395 |
396 | if [ $phase_status = 'FAILED' ]
397 | then
398 |
399 | echo -e "${RED}${phase_status}!!!${NC}"
400 | echo -e "${RED}Please, have a look at cloudwatch logs to understand why the codebuild failed, fix it and re-run the deployment.${NC}"
401 | break
402 |
403 | else
404 |
405 | echo -e "${YELLOW}${build_status}${NC}"
406 | echo -e "${GREEN}${phase_status}${NC}"
407 | break
408 | fi
409 |
410 | elif [ $build_status = 'FALIED' ]
411 | then
412 |
413 | echo -e "${RED}${build_status}!!!${NC}"
414 | echo -e "${RED}Please, have a look at cloudwatch logs to understand why the codebuild failed, fix it and re-run the deployment.${NC}"
415 |
416 | else
417 | echo -e "${RED}${build_status}!!!${NC}"
418 | echo -e "${RED}Something went wrong...Please hava a look at cloudwatchlogs to understand what happened.${NC}"
419 | break
420 |
421 | fi
422 |
423 | done
424 |
425 | }
426 |
427 | ################################################################################
428 | # IAM Roles and Policies - ECS/LAMBDA/DYNAMODB #
429 | ################################################################################
430 |
431 | function iam_resources () {
432 |
433 | # This function will create all IAM resources required by ivs-webrtc
434 |
435 | # Test if exists
436 | aws iam get-role --role-name ivs-ecs-execution-role > /dev/null 2>&1 && iam='OK' || iam='NOK'
437 | sleep 2
438 |
439 | if [ $iam = 'NOK' ]
440 | then
441 |
442 | # Create the Amazon ECS execution role that will be used on our ECS Container.
443 | echo -e "${GREEN}Creating Amazon ECS execution role...${NC}"
444 | aws iam create-role --role-name ivs-ecs-execution-role --assume-role-policy-document file://json_configs/ivs_ecs_trust_policy.json \
445 | | jq '.Role.Arn' | sed 's/"//g' > ./temp_files/ivs_ecs_execution_role_arn.txt
446 | sleep 2
447 |
448 | else
449 |
450 | aws iam get-role --role-name ivs-ecs-execution-role | jq '.Role.Arn' | sed 's/"//g' > ./temp_files/ivs_ecs_execution_role_arn.txt
451 | sleep 2
452 |
453 | fi
454 |
455 | unset iam
456 |
457 | # Attach the required policies to Amazon ECS execution role you just created.
458 | echo -e "${GREEN}Attaching the required policies to Amazon ECS execution role...${NC}"
459 | aws iam attach-role-policy --role-name ivs-ecs-execution-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
460 | sleep 2
461 | aws iam attach-role-policy --role-name ivs-ecs-execution-role --policy-arn arn:aws:iam::aws:policy/AWSOpsWorksCloudWatchLogs
462 | sleep 2
463 |
464 | # Test if exists
465 | aws iam get-role --role-name ivs-lambda-role > /dev/null 2>&1 && iam='OK' || iam='NOK'
466 | sleep 2
467 |
468 | if [ $iam = 'NOK' ]
469 | then
470 |
471 | # Create the AWS Lambda execution role that will allow lambda to access the required AWS resources.
472 | echo -e "${GREEN}Creating the AWS Lambda execution role that will allow lambda to access the required AWS resources${NC}"
473 | aws iam create-role --role-name ivs-lambda-role --assume-role-policy-document file://json_configs/ivs_lambda_trust_policy.json > /dev/null
474 | sleep 2
475 |
476 | fi
477 |
478 | unset iam
479 |
480 | # Attach the required policies to AWS Lambda execution role you just created.
481 | echo -e "${GREEN}Attaching AWSLambdaBasicExecutionRole policy to AWS Lambda execution role...${NC}"
482 | aws iam attach-role-policy --role-name ivs-lambda-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
483 | sleep 2
484 | echo -e "${GREEN}Attaching AmazonEC2ReadOnlyAccess policy to AWS Lambda execution role...${NC}"
485 | aws iam attach-role-policy --role-name ivs-lambda-role --policy-arn arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
486 | sleep 2
487 |
488 | iam=$(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_dynamodb")' | jq '.Arn' | sed 's/"//g')
489 |
490 | # If the policy do not exist, it should be created..Otherwise, it will just attach the policy
491 | if [ ! $iam ]
492 | then
493 |
494 | echo -e "${GREEN}Attaching ivs_dynamodb policy to AWS Lambda execution role...${NC}"
495 | aws iam create-policy --policy-name ivs_dynamodb --policy-document file://json_configs/ivs_lambda_dynamodb_policy.json | jq '.Policy.Arn' | sed 's/"//g' > ./temp_files/lambda_policy_arn.txt
496 | sleep 2
497 | aws iam attach-role-policy --role-name ivs-lambda-role --policy-arn $(cat ./temp_files/lambda_policy_arn.txt)
498 | sleep 2
499 |
500 | else
501 |
502 | echo -e "${GREEN}Attaching ivs_dynamodb policy to AWS Lambda execution role...${NC}"
503 | aws iam attach-role-policy --role-name ivs-lambda-role --policy-arn $(aws iam list-policies --scope Local | jq '.Policies[] | select(.PolicyName=="ivs_dynamodb")' | jq '.Arn' | sed 's/"//g')
504 | sleep 2
505 |
506 | fi
507 |
508 | }
509 |
510 |
511 | ################################################################################
512 | # Lambda Resources #
513 | ################################################################################
514 |
515 | function lambda_resources () {
516 |
517 | unset lambda
518 |
519 | # This function will create all lambda resources required by ivs-webrtc
520 |
521 | #Test if exists
522 | aws iam get-role --role-name ivs-lambda-role > /dev/null 2>&1 && lambda='OK' || lambda='NOK'
523 | sleep 2
524 |
525 | if [ $lambda = 'OK' ]
526 | then
527 |
528 | #Creates lambda.json with our lambda configuration
529 | echo -e "${GREEN}Creating the lambda.json with our lambda configuration...${NC}"
530 | lambda_role_arn=$(aws iam get-role --role-name ivs-lambda-role | jq '.Role.Arn' | sed 's/"//g')
531 | jq --arg v "$lambda_role_arn" '. |= . + {"Role":$v}' ./json_models/lambda_model.json > ./json_configs/lambda.json
532 | sleep 2
533 |
534 | else
535 |
536 | error_exit 'The ivs-lambda-role was not found! Please re-run ./deployment.sh deploy iam'
537 |
538 | fi
539 |
540 | unset lambda
541 | sleep 10
542 |
543 | #Test if exists
544 | aws lambda get-function --function-name ivs-ip-register > /dev/null 2>&1 && lambda='OK' || lambda='NOK'
545 | sleep 2
546 |
547 | if [ $lambda = 'NOK' ]
548 | then
549 |
550 | #Creates the ivs-ip-register lambda function
551 | echo -e "${GREEN}Creating the the ivs-ip-register lambda function...${NC}"
552 | zip lambda.zip lambda_function.py
553 | aws lambda create-function --cli-input-json file://json_configs/lambda.json --zip-file fileb://lambda.zip > /dev/null
554 | sleep 2
555 | fi
556 | }
557 |
558 | ################################################################################
559 | # DynamoDB Resources #
560 | ################################################################################
561 |
562 | function dynamodb_resources () {
563 |
564 | # Ths function will create all dynamodb resources required by ivs-webrtc
565 |
566 | table=$(aws dynamodb list-tables | sed -n -e '/.*ivs/p' | sed -e 's/ //g;s/"//g;s/,//g')
567 | sleep 2
568 |
569 | if [ ! -z $table ]
570 | then
571 |
572 | #Creates the Amazon DynamoDB ISS-task-dns-track-dev table
573 | echo -e "${GREEN}Creating the Amazon DynamoDB ISS-task-dns-track-dev table...${NC}"
574 | aws dynamodb create-table --cli-input-json file://json_configs/dynamodb_table.json > /dev/null
575 | sleep 2
576 | aws dynamodb wait table-exists --table-name ISS-task-dns-track-dev
577 |
578 | #Populates the Amazon DynamoDB ISS-task-dns-track-dev table with the initial values
579 | echo -e "${GREEN}Populating the Amazon DynamoDB ISS-task-dns-track-dev table...${NC}"
580 | aws dynamodb batch-write-item --request-items file://json_configs/ivs_dynamodb_populate.json > /dev/null
581 | sleep 2
582 |
583 | else
584 |
585 | #Populates the Amazon DynamoDB ISS-task-dns-track-dev table with the initial values
586 | echo -e "${GREEN}Populating the Amazon DynamoDB ISS-task-dns-track-dev table...${NC}"
587 | aws dynamodb batch-write-item --request-items file://json_configs/ivs_dynamodb_populate.json > /dev/null
588 | sleep 2
589 |
590 | fi
591 | }
592 |
593 | ################################################################################
594 | # Security Groups #
595 | ################################################################################
596 |
597 | function vpc_resources () {
598 |
599 | # This function will create the security group required by ivs-webrtc
600 |
601 | unset sg
602 |
603 | # Test if exists
604 | aws ec2 create-security-group --group-name ivs-sg --description "IVS WetRTC Security Group" --vpc-id $(aws ec2 describe-vpcs | jq '.Vpcs[] | select(.IsDefault)' | jq '.VpcId' | sed 's/"//g') > /dev/null 2>&1 && sg='OK' || sg='NOK'
605 | sleep 2
606 |
607 | if [ sg = 'NOK' ]
608 | then
609 |
610 | #The Security Group'
611 | echo -e "${GREEN}Creating the ivs-sg security group...${NC}"
612 | aws ec2 create-security-group --group-name ivs-sg --description "IVS WetRTC Security Group" --vpc-id $(aws ec2 describe-vpcs | jq '.Vpcs[] | select(.IsDefault)' | jq '.VpcId' | sed 's/"//g') | jq '.GroupId' | sed 's/"//g'
613 | sleep 2
614 | aws ec2 describe-security-groups | jq '.SecurityGroups[] | select(.GroupName=="ivs-sg")' | jq '.GroupId' | sed 's/"//g' > ./temp_files/ivs_sg.txt
615 | sleep 2
616 |
617 | echo -e "${GREEN}Configuring ivs-sg security group...${NC}"
618 | while read line; do aws ec2 authorize-security-group-ingress --group-id $(cat ./temp_files/ivs_sg.txt) --protocol tcp --port $line --cidr 0.0.0.0/0; done < ./json_models/ivs_ports.txt
619 |
620 | else
621 |
622 | aws ec2 describe-security-groups | jq '.SecurityGroups[] | select(.GroupName=="ivs-sg")' | jq '.GroupId' | sed 's/"//g' > ./temp_files/ivs_sg.txt
623 | sleep 2
624 |
625 | echo -e "${GREEN}Applying HTTP rule to ivs-sg security group...${NC}"
626 | aws ec2 authorize-security-group-ingress --group-id $(cat ./temp_files/ivs_sg.txt) --protocol tcp --port 80 --cidr 0.0.0.0/0
627 | sleep 2
628 |
629 | echo -e "${GREEN}Applying HTTPS rule to ivs-sg security group...${NC}"
630 | aws ec2 authorize-security-group-ingress --group-id $(cat ./temp_files/ivs_sg.txt) --protocol tcp --port 443 --cidr 0.0.0.0/0
631 | sleep 2
632 |
633 | fi
634 | }
635 |
636 | ################################################################################
637 | # ECS Resources #
638 | ################################################################################
639 |
640 | function fargate_resources () {
641 |
642 | # This function will create all fargate resources required by ivs-webrtc
643 |
644 | unset ivs_service
645 |
646 | #Creates the Amazon ECS Cluster named ivs
647 | echo -e "${GREEN}Creating the Amazon ECS Cluster named ivs...${NC}"
648 | aws ecs create-cluster --cluster-name ivs | jq '.cluster.clusterArn' | sed 's/"//g' > ./temp_files/ecs_cluster_arn.txt
649 | sleep 2
650 |
651 | #Configures the ivs_task_definition.json file with the correct Amazon ECS execution previously created
652 | echo -e "${GREEN}Configuring the ivs_task_definition.json file with the correct Amazon ECS execution...${NC}"
653 | ecs_role_arn=$(cat ./temp_files/ivs_ecs_execution_role_arn.txt)
654 | ecr_uri=$(cat ./temp_files/ecr_uri.txt)":latest"
655 | jq --arg v "$ecs_role_arn" --arg f "$ecr_uri" '.taskRoleArn = $v | .executionRoleArn = $v | .containerDefinitions[0].image = $f' ./json_models/ivs_task_definition_model.json > ./json_configs/ivs_task_definition.json
656 |
657 | #Creates Amazon ECS Task Definition named ivs-webrtc
658 | echo -e "${GREEN}Creating the Amazon ECS Task Definition named ivs-webrtc...${NC}"
659 | aws ecs register-task-definition --cli-input-json file://json_configs/ivs_task_definition.json > /dev/null
660 | sleep 2
661 |
662 | #Select the proper subnets from your default vpc to be used by Amazon ECS Service
663 | echo -e "${GREEN}Selecting the proper subnets from your default vpc to be used by Amazon ECS Service...${NC}"
664 | aws ec2 describe-subnets --filter Name=vpc-id,Values=$(aws ec2 describe-vpcs | jq '.Vpcs[] | select(.IsDefault)' | jq '.VpcId' | sed 's/"//g') \
665 | --query 'Subnets[?MapPublicIpOnLaunch==`true`].SubnetId' | sed -e '/^$/d;:a;N;$!ba;s/\n//g;s/ //g;s/[][]//g;s/.$//;s/^.//' > ./temp_files/my_subnets.txt
666 | sleep 2
667 |
668 | ivs_service=$(aws ecs list-services --cluster ivs | sed -n -e '/.*ivs/p' | sed -e 's/ //g;s/"//g;s/,//g')
669 | sleep 2
670 |
671 | if [ ! $ivs_service ]
672 | then
673 |
674 | #Creates the template ivs_ecs_service.json to be used by the Amazon ECS Server
675 | echo -e "${GREEN}Creating the template ivs_ecs_service.json to be used by the Amazon ECS Server...${NC}"
676 | store_sgid=$(cat ./temp_files/ivs_sg.txt)
677 | store_subnets=$(sed -e 'N;s/\n//;N;s/\n//;N;s/\n//' ./temp_files/my_subnets.txt | sed -e 'N;s/\n//;s/ //g;s/[][]//g;s/^"//g;s/"$//g')
678 | jq --arg v "$store_subnets" '.networkConfiguration.awsvpcConfiguration |= . + {"subnets":[$v]}' \
679 | ./json_models/ecs_services_model.json | jq --arg v "$store_sgid" '.networkConfiguration.awsvpcConfiguration |= . + {"securityGroups":[$v]}' \
680 | | sed -e 's/\\//g' > ./json_configs/ivs_ecs_service.json
681 |
682 | #Creates the Amazon ECS service named ivs-webrtc
683 | echo -e "${GREEN}Creating the Amazon ECS service named ivs-webrtc...${NC}"
684 | aws ecs create-service --cli-input-json file://json_configs/ivs_ecs_service.json > /dev/null
685 | sleep 2
686 |
687 | else
688 |
689 | error_exit "The ivs-webrtc does exist and cannot be automatically replaced! Try to delete it mannually and re-run this script."
690 |
691 | fi
692 | }
693 |
694 | ################################################################################
695 | # EventBridge Resources #
696 | ################################################################################
697 |
698 | function eventbridge_resources () {
699 |
700 | # This function will create all eventbridge resources required by ivs-webrtc
701 |
702 | unset lambda
703 |
704 | # Test if exists
705 | aws lambda get-function --function-name ivs-ip-register > /dev/null 2>&1 && lambda='OK' || lambda='NOK'
706 | sleep 2
707 |
708 | #Configures the ivs_events_rule.json with the correct ivs service configured in Amazon ECS Cluster
709 | echo -e "${GREEN}Configuring the ivs_events_rule.json with the correct ivs service configured in Amazon ECS Cluster...${NC}"
710 | ecs_arn=$(cat ./temp_files/ecs_cluster_arn.txt)
711 | sed -e "s@ARN_HERE@${ecs_arn}\\\@g" json_models/ivs_events_rule_model.json > ./json_configs/ivs_events_rule.json
712 |
713 | #Create the Amazon EventBridge rule named ip-register
714 | echo -e "${GREEN}Creating the Amazon EventBridge rule named ip-register...${NC}"
715 | aws events put-rule --cli-input-json file://json_configs/ivs_events_rule.json | jq '.RuleArn' | sed 's/"//g' > ./temp_files/ivs_event_rule_arn.txt
716 | sleep 2
717 |
718 | if [ $lambda = 'OK' ]
719 | then
720 |
721 | unset lambda
722 |
723 | #Creating the resource policy template
724 | echo -e "${GREEN}Creating the resource policy template...${NC}"
725 | aws lambda get-function --function-name ivs-ip-register | jq '.Configuration.FunctionArn' | sed 's/"//g' > ./temp_files/ivs_lambda_function_arn.txt
726 | sleep 2
727 |
728 |
729 |
730 | #Adding permission to ip-register rule invoke lambda funtion ivs-ip-register
731 | echo -e "${GREEN}Adding permission to ip-register rule invoke lambda funtion ivs-ip-register...${NC}"
732 | aws lambda add-permission --function-name ivs-ip-register --action lambda:InvokeFunction --statement-id events --principal events.amazonaws.com --source-arn=$(cat ./temp_files/ivs_event_rule_arn.txt) > /dev/null 2>&1 && lambda='OK' || lambda='NOK'
733 | sleep 2
734 |
735 | if [ $lambda = 'NOK' ]
736 | then
737 |
738 | echo -e "${GREEN}Permission was already in place!${NC}"
739 | fi
740 |
741 | #Add lambda function ivs-ip-register as a target to the event rule ip-register
742 | echo -e "${GREEN}Adding lambda function ivs-ip-register as a target to the event rule ip-register...${NC}"
743 | aws events remove-targets --rule ip-register --ids "1" > /dev/null
744 | sleep 2
745 | aws events put-targets --rule ip-register --targets "Id"="1","Arn"="$(cat ./temp_files/ivs_lambda_function_arn.txt)" > /dev/null
746 | sleep 2
747 |
748 | else
749 |
750 | error_exit 'Lambda ivs-ip-register not found! Please re-run ./backend deploy lambda'
751 |
752 | fi
753 |
754 | }
755 |
756 | ################################################################################
757 | # CloudWatch Resources #
758 | ################################################################################
759 |
760 | function cloudwatchlogs_resources () {
761 |
762 | # This function create all cloudwatchlogs resources required by ivs-webrtc
763 |
764 | unset logs
765 |
766 | #Creating cloudwatch log group name for ivs-webrtc tasks
767 | echo -e "${GREEN}Creating cloudwatch log group name for ivs-webrtc tasks...${NC}"
768 | aws logs create-log-group --log-group-name /ecs/ivs-webrtc > /dev/null 2>&1 && logs='OK' || logs='NOK'
769 | sleep 2
770 |
771 | if [ $logs = 'NOK' ]
772 | then
773 |
774 | echo -e "${GREEN}Log Group /ecs/ivs-webrtc OK!${NC}"
775 | fi
776 |
777 | }
778 |
779 | ################################################################################
780 | # Adjust Fargate Tasks #
781 | ################################################################################
782 |
783 | function fargate_adjust_service () {
784 |
785 | # This function will adjust the fargate service ivs-webrtc from 0 to 2 tasks
786 | echo -e "${GREEN}Adjusting fargate service to 2 tasks...${NC}"
787 | aws ecs update-service --cluster ivs --service ivs-webrtc --desired-count 2 > /dev/null
788 | sleep 2
789 |
790 |
791 |
792 | }
793 |
794 | ################################################################################
795 | # Error messages #
796 | ################################################################################
797 |
798 | function error_exit () {
799 |
800 | echo -e "${RED}$1${NC}" 1>&2
801 | exit 1
802 |
803 | }
804 |
805 | ################################################################################
806 | # Options to deploy the backend #
807 | ################################################################################
808 |
809 | function deploy () {
810 |
811 | # This function will receive a argument from the command line to start the cleaning process
812 | # It will be possible to uninstall everything or just pontual services
813 |
814 | option=$1
815 |
816 | case $option in
817 | all)
818 | ecr_resources || { error_exit 'ecr deployment failed!'; }
819 | s3_resources || { error_exit 's3 deployment failed!'; }
820 | codebuild_adjust_templates || { error_exit 'coldbuild templates adjustment failed!'; }
821 | codebuild_iam || { error_exit 'coldbuild roles and policies failed'; }
822 | codebuild_resources || { error_exit 'codebuild deployment failed!'; }
823 | iam_resources || { error_exit 'iam roles deployment failed!'; }
824 | lambda_resources || { error_exit 'lambda function deployment failed!'; }
825 | dynamodb_resources || { error_exit 'dynamodb table deployment failed!'; }
826 | vpc_resources || { error_exit 'security group deployment failed!'; }
827 | fargate_resources || { error_exit 'fargate deployment failed!'; }
828 | eventbridge_resources || { error_exit 'events deployment failed!'; }
829 | cloudwatchlogs_resources || { error_exit 'logs deployment failed!'; }
830 | fargate_adjust_service || { error_exit 'tasks deployment failed!'; }
831 | ;;
832 |
833 | iam)
834 | echo -e "${GREEN}###Deploying iam ivs-webrtc resources###${NC}"
835 | iam_resources
836 | ;;
837 |
838 | lambda)
839 | echo -e "${GREEN}###Deploying lambda ivs-webrtc resources###${NC}"
840 | lambda_resources
841 | ;;
842 |
843 | dynamodb)
844 | echo -e "${GREEN}###Deploying dynamodb ivs-webrtc resources###${NC}"
845 | dynamodb_resources
846 | ;;
847 |
848 | sg)
849 | echo -e "${GREEN}###Deploying security group ivs-webrtc resources###${NC}"
850 | vpc_resources
851 | ;;
852 |
853 | fargate)
854 | echo -e "${GREEN}###Deploying fargate ivs-webrtc resources###${NC}"
855 | fargate_resources
856 | ;;
857 |
858 | events)
859 | echo -e "${GREEN}###Deploying eventbridge ivs-webrtc resources###${NC}"
860 | eventbridge_resources
861 | ;;
862 |
863 | logs)
864 | echo -e "${GREEN}###Deploying cloudwatchlogs ivs-webrtc resources###${NC}"
865 | cloudwatchlogs_resources
866 | ;;
867 |
868 | tasks)
869 | echo -e "${GREEN}###Deploying tasks ivs-webrtc resources###${NC}"
870 | fargate_adjust_service
871 | ;;
872 |
873 | ecr)
874 | echo -e "${GREEN}###Deploying ecr resources###${NC}"
875 | ecr_resources
876 | ;;
877 |
878 | s3)
879 | echo -e "${GREEN}###Deploying s3 resources###${NC}"
880 | s3_resources
881 | ;;
882 |
883 | templates)
884 | echo -e "${GREEN}###Adjusting codebuild templates###${NC}"
885 | codebuild_adjust_templates
886 | ;;
887 |
888 | codebuild_rp)
889 | echo -e "${GREEN}###Deploying codebuild roles and policies###${NC}"
890 | codebuild_iam
891 | ;;
892 |
893 | codebuild)
894 | echo -e "${GREEN}###Deploying codebuild resources###${NC}"
895 | codebuild_resources
896 | ;;
897 |
898 |
899 | *)
900 | echo -e "${RED}This option is not valid!${NC}"
901 | ;;
902 | esac
903 |
904 | }
905 |
906 | # Enables script arguments
907 | "$@"
908 |
909 | # Prints help in case of calling the script without arguments
910 | if [ -z $1 ]
911 | then
912 |
913 | help
914 | fi
915 |
--------------------------------------------------------------------------------
/backend/json_configs/dynamodb_table.json:
--------------------------------------------------------------------------------
1 | {
2 | "AttributeDefinitions": [
3 | {
4 | "AttributeName": "role",
5 | "AttributeType": "S"
6 | }
7 | ],
8 | "TableName": "ISS-task-dns-track-dev",
9 | "KeySchema": [
10 | {
11 | "AttributeName": "role",
12 | "KeyType": "HASH"
13 | }
14 | ],
15 | "ProvisionedThroughput": {
16 | "ReadCapacityUnits": 5,
17 | "WriteCapacityUnits": 5
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/backend/json_configs/ivs_codebuild_ecr_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Action": [
6 | "ecr:BatchCheckLayerAvailability",
7 | "ecr:CompleteLayerUpload",
8 | "ecr:GetAuthorizationToken",
9 | "ecr:InitiateLayerUpload",
10 | "ecr:PutImage",
11 | "ecr:UploadLayerPart"
12 | ],
13 | "Resource": "*",
14 | "Effect": "Allow"
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/backend/json_configs/ivs_dynamodb_populate.json:
--------------------------------------------------------------------------------
1 | {
2 | "ISS-task-dns-track-dev": [
3 | {
4 | "PutRequest": {
5 | "Item": {
6 | "role": {"S": "primary"},
7 | "eventid": {"S": "ppp"},
8 | "replaced": {"S": "yes"},
9 | "dns": {"S": "ppp"}
10 | }
11 | }
12 | },
13 | {
14 | "PutRequest": {
15 | "Item": {
16 | "role": {"S": "secondary"},
17 | "eventid": {"S": "sss"},
18 | "replaced": {"S": "yes"},
19 | "dns": {"S": "yes"}
20 | }
21 | }
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/backend/json_configs/ivs_ecs_trust_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2008-10-17",
3 | "Statement": [
4 | {
5 | "Sid": "",
6 | "Effect": "Allow",
7 | "Principal": {
8 | "Service": "ecs-tasks.amazonaws.com"
9 | },
10 | "Action": "sts:AssumeRole"
11 | }
12 | ]
13 | }
--------------------------------------------------------------------------------
/backend/json_configs/ivs_lambda_dynamodb_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Sid": "VisualEditor0",
6 | "Effect": "Allow",
7 | "Action": [
8 | "dynamodb:PutItem",
9 | "dynamodb:DeleteItem",
10 | "dynamodb:GetItem",
11 | "dynamodb:Query",
12 | "dynamodb:UpdateItem"
13 | ],
14 | "Resource": [
15 | "arn:aws:dynamodb:*:*:table/ISS-task-dns-track-dev",
16 | "arn:aws:dynamodb:*:*:table/ISS-task-dns-track-dev/index/*"
17 | ]
18 | }
19 | ]
20 | }
--------------------------------------------------------------------------------
/backend/json_configs/ivs_lambda_resource_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Id": "default",
4 | "Statement": [
5 | {
6 | "Sid": "ivs-webrtc-deployment",
7 | "Effect": "Allow",
8 | "Principal": {
9 | "Service": "events.amazonaws.com"
10 | },
11 | "Action": "lambda:InvokeFunction",
12 | "Resource": "arn:aws:lambda:us-east-1:454679031165:function:ivs-ip-register",
13 | "Condition": {
14 | "ArnLike": {
15 | "AWS:SourceArn": "arn:aws:events:us-east-1:454679031165:rule/ip-register"
16 | }
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/backend/json_configs/ivs_lambda_trust_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Principal": {
7 | "Service": "lambda.amazonaws.com"
8 | },
9 | "Action": "sts:AssumeRole"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/backend/json_configs/ivs_webrtc_codebuild_trust_policy.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Principal": {
7 | "Service": "codebuild.amazonaws.com"
8 | },
9 | "Action": "sts:AssumeRole"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/backend/json_models/ecs_services_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "cluster": "ivs",
3 | "serviceName": "ivs-webrtc",
4 | "taskDefinition": "ivs-webrtc",
5 | "desiredCount": 0,
6 | "launchType": "FARGATE",
7 | "schedulingStrategy": "REPLICA",
8 | "platformVersion": "LATEST",
9 | "networkConfiguration": {
10 | "awsvpcConfiguration": {
11 | "subnets": [ ],
12 | "securityGroups": [
13 | "ivs-sg"
14 | ],
15 | "assignPublicIp": "ENABLED"
16 | }
17 | },
18 | "deploymentController": {
19 | "type": "ECS"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/json_models/ivs_codebuild_base_policy_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Resource": [
7 | "arn:aws:logs:us-east-1:REPLACE_ACCOUNTID:log-group:/aws/codebuild/ivs-webrtc",
8 | "arn:aws:logs:us-east-1:REPLACE_ACCOUNTID:log-group:/aws/codebuild/ivs-webrtc:*"
9 | ],
10 | "Action": [
11 | "logs:CreateLogGroup",
12 | "logs:CreateLogStream",
13 | "logs:PutLogEvents"
14 | ]
15 | },
16 | {
17 | "Effect": "Allow",
18 | "Resource": [
19 | "arn:aws:s3:::codepipeline-us-east-1-*"
20 | ],
21 | "Action": [
22 | "s3:PutObject",
23 | "s3:GetObject",
24 | "s3:GetObjectVersion",
25 | "s3:GetBucketAcl",
26 | "s3:GetBucketLocation"
27 | ]
28 | },
29 | {
30 | "Effect": "Allow",
31 | "Action": [
32 | "codebuild:CreateReportGroup",
33 | "codebuild:CreateReport",
34 | "codebuild:UpdateReport",
35 | "codebuild:BatchPutTestCases",
36 | "codebuild:BatchPutCodeCoverages"
37 | ],
38 | "Resource": [
39 | "arn:aws:codebuild:us-east-1:REPLACE_ACCOUNTID:report-group/ivs-webrtc-*"
40 | ]
41 | }
42 | ]
43 | }
--------------------------------------------------------------------------------
/backend/json_models/ivs_codebuild_log_policy_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Resource": [
7 | "arn:aws:logs:us-east-1:REPLACE_ACCOUNTID:log-group:*"
8 | ],
9 | "Action": [
10 | "logs:CreateLogGroup",
11 | "logs:CreateLogStream",
12 | "logs:PutLogEvents"
13 | ]
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/backend/json_models/ivs_codebuild_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ivs-webrtc",
3 | "source": {
4 | "type": "S3",
5 | "location": "REPLACE_BUCKET/docker.zip"
6 | },
7 | "artifacts": {
8 | "type": "NO_ARTIFACTS"
9 | },
10 | "environment": {
11 | "type": "LINUX_CONTAINER",
12 | "image": "aws/codebuild/standard:4.0",
13 | "computeType": "BUILD_GENERAL1_SMALL",
14 | "environmentVariables": [
15 | {
16 | "name": "AWS_DEFAULT_REGION",
17 | "value": "us-east-1"
18 | },
19 | {
20 | "name": "AWS_ACCOUNT_ID",
21 | "value": "REPLACE_ACCOUNTID"
22 | },
23 | {
24 | "name": "IMAGE_REPO_NAME",
25 | "value": "ivs-webrtc"
26 | },
27 | {
28 | "name": "IMAGE_TAG",
29 | "value": "latest"
30 | }
31 | ],
32 | "privilegedMode": true
33 | },
34 | "serviceRole": "REPLACE_ROLE",
35 | "encryptionKey": "arn:aws:kms:us-east-1:REPLACE_ACCOUNTID:alias/aws/s3"
36 | }
--------------------------------------------------------------------------------
/backend/json_models/ivs_codebuild_s3_policy_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Action": [
7 | "s3:GetObject",
8 | "s3:GetObjectVersion"
9 | ],
10 | "Resource": [
11 | "arn:aws:s3:::REPLACE_BUCKET/docker.zip",
12 | "arn:aws:s3:::REPLACE_BUCKET/docker.zip/*"
13 | ]
14 | },
15 | {
16 | "Effect": "Allow",
17 | "Resource": [
18 | "arn:aws:s3:::REPLACE_BUCKET"
19 | ],
20 | "Action": [
21 | "s3:ListBucket",
22 | "s3:GetBucketAcl",
23 | "s3:GetBucketLocation"
24 | ]
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/backend/json_models/ivs_codebuild_vpc_policy_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Statement": [
4 | {
5 | "Effect": "Allow",
6 | "Action": [
7 | "ec2:CreateNetworkInterface",
8 | "ec2:DescribeDhcpOptions",
9 | "ec2:DescribeNetworkInterfaces",
10 | "ec2:DeleteNetworkInterface",
11 | "ec2:DescribeSubnets",
12 | "ec2:DescribeSecurityGroups",
13 | "ec2:DescribeVpcs"
14 | ],
15 | "Resource": "*"
16 | },
17 | {
18 | "Effect": "Allow",
19 | "Action": [
20 | "ec2:CreateNetworkInterfacePermission"
21 | ],
22 | "Resource": "arn:aws:ec2:us-east-1:REPLACE_ACCOUNTID:network-interface/*",
23 | "Condition": {
24 | "StringEquals": {
25 | "ec2:Subnet": [
26 | "arn:aws:ec2:us-east-1:REPLACE_ACCOUNTID:subnet/REPLACE_SUBNET0",
27 | "arn:aws:ec2:us-east-1:REPLACE_ACCOUNTID:subnet/REPLACE_SUBNET1",
28 | "arn:aws:ec2:us-east-1:REPLACE_ACCOUNTID:subnet/REPLACE_SUBNET2"
29 | ],
30 | "ec2:AuthorizedService": "codebuild.amazonaws.com"
31 | }
32 | }
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/backend/json_models/ivs_events_rule_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Name": "ip-register",
3 | "EventPattern": "{\n \"source\": [\"aws.ecs\"],\n \"detail-type\": [\"ECS Task State Change\"],\n \"detail\": {\n \"clusterArn\": [\"ARN_HERE"],\n \"lastStatus\": [{\n \"anything-but\": [\"PENDING\", \"PROVISIONING\", \"STOPPED\",\"DEPROVISIONING\"]\n }]\n }\n}",
4 | "State": "ENABLED",
5 | "EventBusName": "default"
6 | }
7 |
--------------------------------------------------------------------------------
/backend/json_models/ivs_lambda_resource_policy_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": "2012-10-17",
3 | "Id": "default",
4 | "Statement": [
5 | {
6 | "Sid": "ivs-webrtc-deployment",
7 | "Effect": "Allow",
8 | "Principal": {
9 | "Service": "events.amazonaws.com"
10 | },
11 | "Action": "lambda:InvokeFunction",
12 | "Resource": "lambda_arn",
13 | "Condition": {
14 | "ArnLike": {
15 | "AWS:SourceArn": "rule_arn"
16 | }
17 | }
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/backend/json_models/ivs_ports.txt:
--------------------------------------------------------------------------------
1 | 80
2 | 443
3 |
--------------------------------------------------------------------------------
/backend/json_models/ivs_task_definition_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "containerDefinitions": [
3 | {
4 | "name": "ivs-webrtc",
5 | "image": "862418150245.dkr.ecr.us-east-1.amazonaws.com/ivs-webrtc:latest",
6 | "portMappings": [
7 | {
8 | "containerPort": 80,
9 | "hostPort": 80,
10 | "protocol": "tcp"
11 | }
12 | ],
13 | "essential": true,
14 | "readonlyRootFilesystem": false,
15 | "logConfiguration": {
16 | "logDriver": "awslogs",
17 | "options": {
18 | "awslogs-group": "/ecs/ivs-webrtc",
19 | "awslogs-region": "us-east-1",
20 | "awslogs-stream-prefix": "ecs"
21 | }
22 | }
23 | }
24 | ],
25 | "family": "ivs-webrtc",
26 | "taskRoleArn": "arn:aws:iam::862418150245:role/ecsTask",
27 | "executionRoleArn": "arn:aws:iam::862418150245:role/ecsTask",
28 | "networkMode": "awsvpc",
29 | "requiresCompatibilities": [
30 | "FARGATE"
31 | ],
32 | "cpu": "256",
33 | "memory": "512"
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/backend/json_models/lambda_model.json:
--------------------------------------------------------------------------------
1 | {
2 | "FunctionName": "ivs-ip-register",
3 | "Runtime": "python3.8",
4 | "Role": "arn:aws:iam::862418150245:role/service-role/ivs-ip-register-role-992spxkd",
5 | "Handler": "lambda_function.lambda_handler",
6 | "Description": "",
7 | "Timeout": 3,
8 | "MemorySize": 128
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/backend/lambda_function.py:
--------------------------------------------------------------------------------
1 | import json
2 | import boto3
3 |
4 | # Set the EC2 Client
5 | ec2 = boto3.resource('ec2')
6 |
7 | # Set DynamoDB Client
8 | dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
9 |
10 | # Set DynamoDB Table
11 | table = dynamodb.Table('ISS-task-dns-track-dev')
12 |
13 | def get_values():
14 |
15 | # This function will: 1 - Populate DynamoDB if there is not items. 2 - Retrive the items from DynamoDB
16 |
17 | # Defines the list that will store metadata servers from DynamoDB Tale
18 | servers = []
19 |
20 | # This is a list that will be set based on DynamoDB queries.
21 | items = []
22 |
23 | # This will query dynamodb table looking for a pk named primary
24 | servers.append(table.get_item(
25 | Key={'role':'primary'}
26 | ))
27 |
28 | # In case of not finding pk primary, it will create a item with default values
29 | if not servers[0].get('Item'):
30 | set_default_values('primary','yes','ppp','ppp')
31 | else:
32 | items.append(servers[0].get('Item'))
33 |
34 | # This will query dynamodb table looking for a pk named secondary
35 | servers.append(table.get_item(
36 | Key={'role':'secondary'}
37 | ))
38 |
39 | # In case of not finding pk secondary, it will create a item with default values
40 | if not servers[1].get('Item'):
41 | set_default_values('secondary','yes','sss','sss')
42 | else:
43 | items.append(servers[1].get('Item'))
44 |
45 | return items
46 |
47 |
48 | def set_default_values(role,replaced,default_id,dns):
49 |
50 | # This function will be used to set default values on DynamoDB table.
51 |
52 | items = []
53 |
54 | table.put_item(
55 | Item={
56 | 'role': role,
57 | 'replaced': replaced,
58 | 'eventid': default_id,
59 | 'dns': dns
60 | }
61 | )
62 |
63 | def update_values(role, replaced, eventid, dns='not_defined'):
64 |
65 | # This function will be used to update server values on DynamoDB table.
66 |
67 | table.update_item(
68 | Key={ 'role': role},
69 | UpdateExpression="set replaced=:r, eventid=:e, dns=:d",
70 | ExpressionAttributeValues={
71 | ':r': replaced,
72 | ':e': eventid,
73 | ':d': dns
74 | }
75 | )
76 |
77 | def get_public_ip(eni_id):
78 |
79 | # This function will retrive the fqdn of the servers
80 | # InvalidNetworkInterfaceID.NotFound as error:
81 |
82 | try:
83 | eni = ec2.NetworkInterface(eni_id)
84 | fqdn = eni.association_attribute['PublicDnsName']
85 | return fqdn
86 | except:
87 | print('The Network Interface does not exist! This lambda function will finish here')
88 | return None
89 |
90 | def set_to_replace(primary,secondary,public_dns_name,eventid):
91 |
92 | # This function will set the servers to be replaces by futures ones
93 |
94 | # This will configure the metadata of the primary server to be replaced by the new future server
95 | if (primary.get('dns') == public_dns_name) and (primary.get('role') == 'primary') and (primary.get('replaced') == 'no') and (primary.get('eventid') != eventid) and (secondary.get('eventid') != eventid):
96 |
97 | update_values('primary','yes',eventid)
98 | #print('First IF')
99 | return None
100 |
101 | # This will configure the metadata of the primary server to be replaced by the new future server
102 | elif (secondary.get('dns') == public_dns_name) and (secondary.get('role') == 'secondary') and (secondary.get('replaced') == 'no') and (secondary.get('eventid') != eventid) and (primary.get('eventid') != eventid):
103 |
104 | update_values('secondary','yes',eventid)
105 | #print('Second IF')
106 | return None
107 |
108 |
109 | def set_new_server(primary,secondary,public_dns_name,eventid):
110 |
111 | # This function will set the metadata for the new primary or secondary server
112 |
113 | # This will register the metadata of the new primary server
114 | if (primary.get('role') == 'primary') and (primary.get('replaced') == 'yes') and (primary.get('eventid') != eventid) and (secondary.get('eventid') != eventid):
115 |
116 | update_values('primary','no',eventid, public_dns_name)
117 | print('Primary DNS Updated!!!!')
118 | return None
119 |
120 | # This will register the metadata of the new secondary server
121 | elif (secondary.get('role') == 'secondary') and (secondary.get('replaced') == 'yes') and (secondary.get('eventid') != eventid) and (primary.get('eventid') != eventid):
122 |
123 | update_values('secondary','no',eventid, public_dns_name)
124 | print('Secondary DNS Updated!!!!')
125 | return None
126 |
127 |
128 |
129 | def lambda_handler(event, context):
130 |
131 | # Stores the event_id of the event
132 | event_id = event['id']
133 |
134 | # Debug
135 | print('Tracking all Status')
136 | print(event)
137 |
138 | # This will be valid if the server transition from running to stop stage.
139 | if (event['detail']['lastStatus'] == 'RUNNING') and (event['detail']['desiredStatus'] == 'STOPPED'):
140 | eni_id = event['detail']['attachments'][0]['details'][1]['value']
141 | public_dns_name = get_public_ip(eni_id)
142 |
143 | # Calls get_values to retrive server metadata
144 | items = get_values()
145 |
146 | # Calls set_to_replace when a server dies
147 | set_to_replace(items[0],items[1],public_dns_name,event_id)
148 |
149 | # This will run if the new server comes online
150 | elif (event['detail']['lastStatus'] == 'RUNNING') and (event['detail']['desiredStatus'] == 'RUNNING'):
151 | eni_id = event['detail']['attachments'][0]['details'][1]['value']
152 | public_dns_name = get_public_ip(eni_id)
153 |
154 | # Calls get_values to retrive server metadata
155 | items = get_values()
156 |
157 | # Calls set_new_server function
158 | set_new_server(items[0],items[1],public_dns_name,event_id)
159 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "transwrap-proxy",
3 | "version": "0.2.0",
4 | "description": "video proxy webm to rtmps",
5 | "main": "transwrap.js",
6 | "dependencies": {
7 | "express": "^4.17.1",
8 | "ws": "^7.4.5"
9 | },
10 | "devDependencies": {
11 | "nodemon": "^2.0.16"
12 | },
13 | "scripts": {
14 | "start": "node transwrap_https.js",
15 | "start-test": "node transwrap_http.js",
16 | "startDev": "nodemon transwrap_http.js",
17 | "startDevs": "nodemon transwrap_https.js"
18 | },
19 | "author": "osmarb marlonpc",
20 | "license": "MIT"
21 | }
22 |
--------------------------------------------------------------------------------
/backend/temp_files/.gitignore:
--------------------------------------------------------------------------------
1 | *.txt
2 |
--------------------------------------------------------------------------------
/backend/transwrap_http.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | // transwrap.js http version
5 |
6 | /// import block
7 | const http = require("http");
8 | //const fs = require('fs');
9 | const WebSocket = require("ws");
10 | const express = require("express");
11 | const callff = require("child_process");
12 |
13 | /// app socket definition
14 | const app = express();
15 | const port = 3004;
16 | const servertype = "HTTP";
17 | const home = "/wwww"; /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
18 |
19 | // https certificates // Be sure to generate those before running
20 | /*
21 | const newCert = 'cert.pem';
22 | const newKey = 'key.pem'
23 | const certicates = {
24 | key: fs.readFileSync(newKey),
25 | cert: fs.readFileSync(newCert)
26 | };
27 | */
28 |
29 | // wrapper server
30 | const transwraper = http.createServer(app).listen(port, () => {
31 | console.log(`Listening on port: ${port} ${servertype}`);
32 | });
33 |
34 | // websocket creation
35 | const wsRef = new WebSocket.Server({ server: transwraper });
36 |
37 | app.use((req, res, next) => {
38 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
39 | return next();
40 | });
41 |
42 | app.use(express.static(__dirname + home));
43 |
44 | // Streaming
45 | wsRef.on("connection", (ws, req) => {
46 | console.log("Loop 1");
47 | ws.send("MSG: Connected to server"); /// Send this message to the app frontend
48 | console.log(`Got connection, URL: ${req.url}`);
49 | ws.on("message", (evt) => {
50 | console.log("Event", evt);
51 | ffmpeg.stdin.write(evt);
52 | });
53 |
54 | ws.on("error", (err) => {
55 | console.log("ERROR on websocket", err);
56 | });
57 |
58 | const rtmpURL = req.url.slice(12);
59 | console.log(`URL seted is ${rtmpURL}`);
60 |
61 | const codec = req.url.split("/")[2];
62 | console.log("CODEC", codec);
63 |
64 | if (codec === "h264") {
65 | console.log("No video transcoding");
66 | var ffArr = [
67 | "-i",
68 | "-",
69 | "-vcodec",
70 | "copy",
71 | "-preset",
72 | "veryfast",
73 | "-tune",
74 | "zerolatency",
75 | "-acodec",
76 | "aac",
77 | "-ar",
78 | "44100",
79 | "-b:a",
80 | "128k",
81 | "-f",
82 | "flv",
83 | rtmpURL,
84 | "-reconnect",
85 | "3",
86 | "-reconnect_at_eof",
87 | "1",
88 | "-reconnect_streamed",
89 | "3",
90 | ];
91 | } else {
92 | console.log("Transcoding true");
93 | //ffmpeg -re -stream_loop -1 -i $VIDEO_FILEPATH -r 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -preset veryfast -x264opts "nal-hrd=cbr:no-scenecut" -minrate 3000 -maxrate 3000 -g 60 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmps://$INGEST_ENDPOINT:443/app/$STREAM_KEY
94 |
95 | var ffArr = [
96 | "-fflags",
97 | "+genpts",
98 | "-i",
99 | "-",
100 | "-r",
101 | "30",
102 | "-c:v",
103 | "libx264",
104 | "-pix_fmt",
105 | "yuv420p",
106 | "-profile:v",
107 | "main",
108 | "-preset",
109 | "veryfast",
110 | "-x264opts",
111 | "nal-hrd=cbr:no-scenecut",
112 | "-minrate",
113 | "3000",
114 | "-maxrate",
115 | "3000",
116 | "-g",
117 | "60",
118 | "-c:a",
119 | "aac",
120 | "-b:a",
121 | "128k",
122 | "-ac",
123 | "2",
124 | "-ar",
125 | "44100",
126 | "-vf",
127 | "scale=1280:720,format=yuv420p",
128 | "-profile:v",
129 | "main",
130 | "-f",
131 | "flv",
132 | rtmpURL,
133 | "-reconnect",
134 | "3",
135 | "-reconnect_at_eof",
136 | "1",
137 | "-reconnect_streamed",
138 | "3",
139 | ];
140 | }
141 |
142 | ws.on("close", (evt) => {
143 | ffmpeg.kill("SIGINT");
144 | console.log(`Connection Closed: ${evt}`);
145 | });
146 |
147 | const ffmpeg = callff.spawn("ffmpeg", ffArr);
148 |
149 | ffmpeg.on("close", (code, signal) => {
150 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
151 | ws.send("Closing Socket"); /// message to front end
152 | ws.terminate();
153 | });
154 |
155 | ffmpeg.stdin.on("error", (e) => {
156 | ws.send(e);
157 | console.log(`FFMPEG ERROR: ${e}`);
158 | ws.terminate();
159 | });
160 |
161 | ffmpeg.stderr.on("data", (data) => {
162 | console.log(`FFMPEG MSG: ${data.toString()}`);
163 | ws.send(data.toString());
164 | });
165 | });
166 |
--------------------------------------------------------------------------------
/backend/transwrap_https.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | // transwrap.js https version
5 |
6 | /// import block
7 | const https = require("https");
8 | const fs = require("fs");
9 | const WebSocket = require("ws");
10 | const express = require("express");
11 | const callff = require("child_process");
12 |
13 | /// app socket definition
14 | const app = express();
15 | const port = 3004;
16 | const servertype = "HTTPS";
17 | const home = "wwww"; /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
18 |
19 | // https certificates // Be sure to generate those before running
20 | const newCert = "cert.pem";
21 | const newKey = "key.pem";
22 | const certicates = {
23 | key: fs.readFileSync(newKey),
24 | cert: fs.readFileSync(newCert),
25 | };
26 |
27 | // wrapper server
28 | const transwraper = https.createServer(certicates, app).listen(port, () => {
29 | console.log(`Listening on port: ${port} ${servertype}`);
30 | });
31 |
32 | // websocket creation
33 | const wsRef = new WebSocket.Server({ server: transwraper });
34 |
35 | app.use((req, res, next) => {
36 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
37 | return next();
38 | });
39 |
40 | app.use(express.static(__dirname + home));
41 |
42 | // Streaming
43 | wsRef.on("connection", (ws, req) => {
44 | console.log("Loop 1");
45 | ws.send("MSG: Connected to server"); /// Send this message to the app frontend
46 | console.log(`Got connection, URL: ${req.url}`);
47 | ws.on("message", (evt) => {
48 | console.log("Event", evt);
49 | ffmpeg.stdin.write(evt);
50 | });
51 |
52 | ws.on("error", (err) => {
53 | console.log("ERROR on websocket", err);
54 | });
55 |
56 | const rtmpURL = req.url.slice(12);
57 | console.log(`URL seted is ${rtmpURL}`);
58 |
59 | const codec = req.url.split("/")[2];
60 | console.log("CODEC", codec);
61 |
62 | if (codec === "h264") {
63 | console.log("No video transcoding");
64 | var ffArr = [
65 | "-i",
66 | "-",
67 | "-vcodec",
68 | "copy",
69 | "-preset",
70 | "veryfast",
71 | "-tune",
72 | "zerolatency",
73 | "-acodec",
74 | "aac",
75 | "-ar",
76 | "44100",
77 | "-b:a",
78 | "128k",
79 | "-f",
80 | "flv",
81 | rtmpURL,
82 | "-reconnect",
83 | "3",
84 | "-reconnect_at_eof",
85 | "1",
86 | "-reconnect_streamed",
87 | "3",
88 | ];
89 | } else {
90 | console.log("Transcoding true");
91 | //ffmpeg -re -stream_loop -1 -i $VIDEO_FILEPATH -r 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -preset veryfast -x264opts "nal-hrd=cbr:no-scenecut" -minrate 3000 -maxrate 3000 -g 60 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmps://$INGEST_ENDPOINT:443/app/$STREAM_KEY
92 |
93 | var ffArr = [
94 | "-fflags",
95 | "+genpts",
96 | "-i",
97 | "-",
98 | "-r",
99 | "30",
100 | "-c:v",
101 | "libx264",
102 | "-pix_fmt",
103 | "yuv420p",
104 | "-profile:v",
105 | "main",
106 | "-preset",
107 | "veryfast",
108 | "-x264opts",
109 | "nal-hrd=cbr:no-scenecut",
110 | "-minrate",
111 | "3000",
112 | "-maxrate",
113 | "3000",
114 | "-g",
115 | "60",
116 | "-c:a",
117 | "aac",
118 | "-b:a",
119 | "128k",
120 | "-ac",
121 | "2",
122 | "-ar",
123 | "44100",
124 | "-vf",
125 | "scale=1280:720,format=yuv420p",
126 | "-profile:v",
127 | "main",
128 | "-f",
129 | "flv",
130 | rtmpURL,
131 | "-reconnect",
132 | "3",
133 | "-reconnect_at_eof",
134 | "1",
135 | "-reconnect_streamed",
136 | "3",
137 | ];
138 | }
139 |
140 | ws.on("close", (evt) => {
141 | ffmpeg.kill("SIGINT");
142 | console.log(`Connection Closed: ${evt}`);
143 | });
144 |
145 | const ffmpeg = callff.spawn("ffmpeg", ffArr);
146 |
147 | ffmpeg.on("close", (code, signal) => {
148 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
149 | ws.send("Closing Socket"); /// message to front end
150 | ws.terminate();
151 | });
152 |
153 | ffmpeg.stdin.on("error", (e) => {
154 | ws.send(e);
155 | console.log(`FFMPEG ERROR: ${e}`);
156 | ws.terminate();
157 | });
158 |
159 | ffmpeg.stderr.on("data", (data) => {
160 | console.log(`FFMPEG MSG: ${data.toString()}`);
161 | ws.send(data.toString());
162 | });
163 | });
164 |
--------------------------------------------------------------------------------
/backend/transwrap_local _peer.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | // transwrap.js http version
5 |
6 | /// import block
7 | const http = require('http');
8 | //const fs = require('fs');
9 | const WebSocket = require('ws');
10 | const express = require('express');
11 | const callff = require('child_process');
12 |
13 | /// app socket definition
14 | const app = express();
15 | const port = 3004
16 | const servertype = 'HTTP'
17 | const home = '/wwww' /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
18 |
19 | // https certificates // Be sure to generate those before running
20 | /*
21 | const newCert = 'cert.pem';
22 | const newKey = 'key.pem'
23 | const certicates = {
24 | key: fs.readFileSync(newKey),
25 | cert: fs.readFileSync(newCert)
26 | };
27 | */
28 |
29 | // wrapper server
30 | const transwraper = http.createServer(app).listen(port, () => {console.log(`Listening on port: ${port} ${servertype}`)})
31 |
32 | // websocket creation
33 | const wsRef = new WebSocket.Server({ server: transwraper});
34 |
35 | app.use((req, res, next) => {
36 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
37 |
38 | wsRef.on('connection', (ws, req) => {
39 | console.log("!!!!!! connection")
40 | })
41 |
42 | wsRef.on('open', (ws, req) => {
43 | console.log("!!!!!! connection")
44 | })
45 |
46 |
47 | return next();
48 |
49 |
50 |
51 | });
52 |
53 | app.use(express.static(__dirname + home));
54 |
55 | // Streaming
56 | wsRef.on('connection', (ws, req) => {
57 | console.log("Loop 1")
58 | //ws.send('MSG: Connected to server'); /// Send this message to the app frontend
59 | console.log(`Got connection, URL: ${req.url}`)
60 | // em testes
61 | if (req.url === '/'){
62 | //writeHead(200, {'Content-Type': 'text/html'});
63 | console.log('é o /')
64 |
65 | ws.on('message', function(message) {
66 | // Broadcast any received message to all clients
67 | console.log('received: %s', message);
68 | ws.broadcast(message);
69 | });
70 |
71 |
72 | ws.broadcast = function(data) {
73 | this.clients.forEach(function(client) {
74 | if(client.readyState === WebSocket.OPEN) {
75 | client.send(data);
76 | }
77 | });
78 | };
79 |
80 | /// em testes
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | } else {
89 | ws.on('message', (evt) => {
90 | console.log('Event', evt);
91 | ffmpeg.stdin.write(evt);
92 | });
93 |
94 | ws.on('error', (err) => {
95 | console.log('ERROR on websocket', err);
96 | });
97 |
98 | const rtmpURL = req.url.slice(7)
99 | console.log(`URL seted is ${rtmpURL}`)
100 |
101 | ws.on('close', (evt) => {
102 | ffmpeg.kill('SIGINT');
103 | console.log(`Connection Closed: ${evt}`)
104 | });
105 |
106 | const ffmpeg = callff.spawn(
107 | 'ffmpeg',
108 | ['-i', '-', '-vcodec', 'copy', '-preset', 'veryfast', '-tune', 'zerolatency', '-acodec', 'aac', '-reconnect', '3', '-reconnect_at_eof', '1', '-reconnect_streamed', '3', '-f', 'flv', rtmpURL
109 | ]);
110 |
111 | ffmpeg.on('close', (code, signal) => {
112 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
113 | ws.send('Closing Socket'); /// message to front end
114 | ws.terminate();
115 | });
116 |
117 | ffmpeg.stdin.on('error', (e) => {
118 | ws.send(e);
119 | console.log(`FFMPEG ERROR: ${e}`)
120 | ws.terminate();
121 | });
122 |
123 | ffmpeg.stderr.on('data', (data) => {
124 | console.log(`FFMPEG MSG: ${data.toString()}`)
125 | ws.send(data.toString());
126 | });
127 | }
128 | });
129 |
--------------------------------------------------------------------------------
/backend/transwrap_local.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | // transwrap.js http version
5 |
6 | /// import block
7 | const http = require("http");
8 | //const fs = require('fs');
9 | const WebSocket = require("ws");
10 | const express = require("express");
11 | const callff = require("child_process");
12 |
13 | /// app socket definition
14 | const app = express();
15 | const port = 3004;
16 | const servertype = "HTTP";
17 | const home = "/wwww"; /// for heath check only, you can also use as standalone server, by moving the frontend folder to the home www
18 |
19 | // https certificates // Be sure to generate those before running
20 | /*
21 | const newCert = 'cert.pem';
22 | const newKey = 'key.pem'
23 | const certicates = {
24 | key: fs.readFileSync(newKey),
25 | cert: fs.readFileSync(newCert)
26 | };
27 | */
28 |
29 | // wrapper server
30 | const transwraper = http.createServer(app).listen(port, () => {
31 | console.log(`Listening on port: ${port} ${servertype}`);
32 | });
33 |
34 | // websocket creation
35 | const wsRef = new WebSocket.Server({ server: transwraper });
36 |
37 | app.use((req, res, next) => {
38 | console.log(`${servertype}:${req.method}:${req.originalUrl}`);
39 | return next();
40 | });
41 |
42 | app.use(express.static(__dirname + home));
43 |
44 | // Streaming
45 | wsRef.on("connection", (ws, req) => {
46 | console.log("Loop 1");
47 | ws.send("MSG: Connected to server"); /// Send this message to the app frontend
48 | console.log(`Got connection, URL: ${req.url}`);
49 | ws.on("message", (evt) => {
50 | console.log("Event", evt);
51 | ffmpeg.stdin.write(evt);
52 | });
53 |
54 | ws.on("error", (err) => {
55 | console.log("ERROR on websocket", err);
56 | });
57 |
58 | const rtmpURL = req.url.slice(12);
59 | console.log(`URL seted is ${rtmpURL}`);
60 |
61 | const codec = req.url.split("/")[2];
62 | console.log("CODEC", codec);
63 |
64 | if (codec === "h264") {
65 | console.log("No video transcoding");
66 | var ffArr = [
67 | "-i",
68 | "-",
69 | "-vcodec",
70 | "copy",
71 | "-preset",
72 | "veryfast",
73 | "-tune",
74 | "zerolatency",
75 | "-acodec",
76 | "aac",
77 | "-ar",
78 | "44100",
79 | "-b:a",
80 | "128k",
81 | "-f",
82 | "flv",
83 | rtmpURL,
84 | "-reconnect",
85 | "3",
86 | "-reconnect_at_eof",
87 | "1",
88 | "-reconnect_streamed",
89 | "3",
90 | ];
91 | } else {
92 | console.log("Transcoding true");
93 | //ffmpeg -re -stream_loop -1 -i $VIDEO_FILEPATH -r 30 -c:v libx264 -pix_fmt yuv420p -profile:v main -preset veryfast -x264opts "nal-hrd=cbr:no-scenecut" -minrate 3000 -maxrate 3000 -g 60 -c:a aac -b:a 160k -ac 2 -ar 44100 -f flv rtmps://$INGEST_ENDPOINT:443/app/$STREAM_KEY
94 |
95 | var ffArr = [
96 | "-fflags",
97 | "+genpts",
98 | "-i",
99 | "-",
100 | "-r",
101 | "30",
102 | "-c:v",
103 | "libx264",
104 | "-pix_fmt",
105 | "yuv420p",
106 | "-profile:v",
107 | "main",
108 | "-preset",
109 | "veryfast",
110 | "-x264opts",
111 | "nal-hrd=cbr:no-scenecut",
112 | "-minrate",
113 | "3000",
114 | "-maxrate",
115 | "3000",
116 | "-g",
117 | "60",
118 | "-c:a",
119 | "aac",
120 | "-b:a",
121 | "128k",
122 | "-ac",
123 | "2",
124 | "-ar",
125 | "44100",
126 | "-vf",
127 | "scale=1280:720,format=yuv420p",
128 | "-profile:v",
129 | "main",
130 | "-f",
131 | "flv",
132 | rtmpURL,
133 | "-reconnect",
134 | "3",
135 | "-reconnect_at_eof",
136 | "1",
137 | "-reconnect_streamed",
138 | "3",
139 | ];
140 | }
141 |
142 | ws.on("close", (evt) => {
143 | ffmpeg.kill("SIGINT");
144 | console.log(`Connection Closed: ${evt}`);
145 | });
146 |
147 | const ffmpeg = callff.spawn("ffmpeg", ffArr);
148 |
149 | ffmpeg.on("close", (code, signal) => {
150 | console.log(`FFMPEG closed, reason ${code} , ${signal}`);
151 | ws.send("Closing Socket"); /// message to front end
152 | ws.terminate();
153 | });
154 |
155 | ffmpeg.stdin.on("error", (e) => {
156 | ws.send(e);
157 | console.log(`FFMPEG ERROR: ${e}`);
158 | ws.terminate();
159 | });
160 |
161 | ffmpeg.stderr.on("data", (data) => {
162 | console.log(`FFMPEG MSG: ${data.toString()}`);
163 | ws.send(data.toString());
164 | });
165 | });
166 |
--------------------------------------------------------------------------------
/backend/uninstall_ivs_backend.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Define colors
4 | RED='\033[0;31m'
5 | GREEN='\033[0;32m'
6 | YELLOW='\033[0;93m'
7 | NC='\033[0m'
8 |
9 | # This is a very simples scrip that uses aws cli + jq + sed to clean your environment by removing all resources deploy by the install_ivs_backend.sh
10 | # If you wish to remove specific resources, go to the boton of this file and comment the function calls you wish to avoid.
11 |
12 | ################################################################################
13 | # Help #
14 | ################################################################################
15 |
16 | function help ()
17 | {
18 | # Display Help
19 | echo -e "${GREEN}This script cleans your environment by removing all backend resources deployed by install_ivs_backend.sh"
20 | echo
21 | echo -e "Syntax: ./uninstall_ivs_backend.sh clean [ all | codebuild | codebuild_rp | s3 | ecr | codebuild | lambda | iam | fargate | sg | events | events | logs | files ]${NC}"
22 | echo -e "${YELLOW}options:"
23 | echo -e "all Cleans the whole environment by removing all resources installed by install_ivs_backend.sh"
24 | echo -e "codebuild Removes the codebuild project"
25 | echo -e "codebuild_rp Removes all roles and policies required by codebuild"
26 | echo -e "s3 Removes S3 bucket"
27 | echo -e "ecr Removes the ECR repository"
28 | echo -e "lambda Removes the lambda function"
29 | echo -e "iam Removes all roles and policies required by lambda,dynamodb and fargate"
30 | echo -e "fargate Removes ECS cluster, task definitions and services"
31 | echo -e "sg Removes the segurity groups required by fargate"
32 | echo -e "events Removes the eventbridge rules and triggers"
33 | echo -e "logs Removes the cloudwatch log group"
34 | echo -e "files Removes the tempoary files\n\n"
35 |
36 | echo -e "${GREEN}Example on how to use this script to deploy the whole backend at once"
37 | echo -e "./uninstall_ivs_backend.sh clean all${NC}\n\n"
38 |
39 | echo -e "${YELLOW}Case you prefer to deploy the backend step by step, deploy the each item following the order showing in this help"
40 | echo -e "Example:"
41 | echo -e "./uninstall_ivs_backend.sh clean codebuild"
42 | echo -e "./install_ivs_backend.sh deploy codebuild_rp"
43 | echo -e "./install_ivs_backend.sh deploy ecr"
44 | echo -e "...and so on...${NC}\n\n"
45 |
46 |
47 | }
48 |
49 | ################################################################################
50 | # Codebuild Deprovisiong #
51 | ################################################################################
52 |
53 |
54 | function codebuild_deprov () {
55 |
56 | # This function deletes the codebuild project
57 |
58 | echo -e "${GREEN}Deleting Codebuild ivs-webrtc project...${NC}"
59 | aws codebuild delete-project --name ivs-webrtc > /dev/null 2>&1 && echo -e "${GREEN}Codebuild ivs-webrtc project deleted.${NC}" \
60 | || echo -e "${RED}It seems you have already deleted the codebuild ivs-webrtc project!!!${NC}"
61 |
62 | }
63 |
64 | ################################################################################
65 | # Codebuild Roles and Policies Deprovisiong #
66 | ################################################################################
67 |
68 | function codebuild_rp_deprov () {
69 |
70 | # This deletes the roles and policies required by codebuild
71 |
72 | # Array that stores all policies arn that should be detached
73 | codebuild_policies=($(cat ./temp_files/ivs_codebuild_log_arn.txt) \
74 | $(cat ./temp_files/ivs_codebuild_s3_arn.txt) \
75 | $(cat ./temp_files/ivs_codebuild_vpc_arn.txt) \
76 | $(cat ./temp_files/ivs_codebuild_base_arn.txt) \
77 | $(cat ./temp_files/ivs_codebuild_ecr_arn.txt))
78 |
79 | # Array that stores all roles that should be deleted
80 | codebuild_roles_to_delete=('ivs-webrtc-codebuild-role')
81 |
82 | # Calls detach_policy passing the policies array as argument
83 | detach_policy ${codebuild_policies[@]} ivs-webrtc-codebuild-role
84 |
85 | # Calls delete_policy passing the policies array as argument
86 | delete_policy ${codebuild_policies[@]}
87 |
88 | # Calls delete_role passing the policies array as argument
89 | delete_role ${codebuild_roles_to_delete[@]}
90 |
91 | unset iam_role
92 |
93 | }
94 |
95 | ################################################################################
96 | # S3 Bucket Deprovisiong #
97 | ################################################################################
98 |
99 | function s3_deprov () {
100 |
101 | # This function removes the S3 bucket required by codebuild
102 |
103 | # Captures the bucket name
104 | s3_bucket=$(cat ./temp_files/s3_bucket.txt)
105 |
106 | # Remove the bucket and all of its objects
107 | echo -e "${GREEN}Removing S3 ${s3_bucket} bucket and objects...${NC}"
108 | aws s3 rb s3://${s3_bucket} --force > /dev/null 2>&1 && echo -e "${GREEN}S3 Bucket ${s3_bucket} deleted${NC}" \
109 | || echo -e "${RED}It seems that ${s3_bucket} bucket was already deleted!!!${NC}"
110 | }
111 |
112 | ################################################################################
113 | # ECR Deprovisiong #
114 | ################################################################################
115 |
116 | function ecr_deprov () {
117 |
118 | # This function deletes the ECR repository required by codebuild
119 |
120 | echo -e "${GREEN}Removing ECR ivs-webrtc repository...${NC}"
121 | aws ecr delete-repository --repository-name ivs-webrtc --force > /dev/null 2>&1 && echo -e "${GREEN}ECR ivs-webrtc repository deleted${NC}" \
122 | || echo -e "${RED}It seems that ivs-webrtc repository was already deleted!!!${NC}"
123 |
124 | }
125 |
126 |
127 | ################################################################################
128 | # EventBridge Deprovisiong #
129 | ################################################################################
130 |
131 | function eventbridge_deprov () {
132 |
133 | # This function will remove the eventbridge rule named ip-register
134 |
135 | echo -e "${GREEN}Deleting the EventBridge rules and targets...${NC}"
136 |
137 | # Check if the resource exist
138 | if [ $(aws events list-rules --name-prefix ip-register | jq '.Rules[0].Name') != "null" ]
139 | then
140 | if [ $(aws events list-targets-by-rule --rule ip-register | jq '.Targets[0].Id') != "null" ]
141 | then
142 | echo -e "${GREEN}Deleting the EventBridge Rule: ip-register...${NC}"
143 | aws events remove-targets --rule ip-register --ids "1" 1> /dev/null
144 |
145 | if $(aws events delete-rule --name ip-register > /dev/null 2>&1);
146 | then
147 | echo -e "${GREEN}EventBridge Rule Deleted!${NC}"
148 | else
149 | error_exit "The EventBrige Rule cannot be automatically deleted because it has dependencies. You may want to try remove it manually."
150 | fi
151 |
152 | else
153 |
154 | echo -e "${RED}Hmm...It seems you have already deleted the target...${NC}"
155 | echo -e "${RED}I will try to delete the event-rule then...${NC}"
156 | aws events delete-rule --name ip-register
157 |
158 | fi
159 |
160 | else
161 |
162 | echo -e "${RED}It seems you have already deleted the ip-register rule!!!${NC}"
163 | fi
164 | }
165 |
166 | ################################################################################
167 | # Fargate Deprovisioning #
168 | ################################################################################
169 |
170 | function fargate_deprov () {
171 |
172 | # Retrive the task definition version
173 | ivs_task_definition=($(aws ecs list-task-definitions | sed -n -e '/.*ivs/p' | sed -e 's/ //g;s/"//g;s/,//g' | sed 's/.*\(ivs.*\).*/\1/'))
174 |
175 | # Test if exists
176 | service_status=$(aws ecs describe-services --cluster ivs --services ivs-webrtc | jq '.services[].status' | sed 's/"//g')
177 | ivs_cluster=$(aws ecs describe-clusters --cluster ivs | jq '.clusters[].status' | sed 's/"//g')
178 |
179 | if [ $(aws ecs describe-services --cluster ivs --services ivs-webrtc | jq '.services[0].desiredCount') != 0 ] && [ $(aws ecs describe-services --cluster ivs --services ivs-webrtc | jq '.services[0].desiredCount') != 'null' ]
180 | then
181 | echo -e "${GREEN}Eliminating tasks from ivs-webrtc service...${NC}"
182 | aws ecs update-service --cluster ivs --service ivs-webrtc --desired-count 0 >> /dev/null
183 |
184 | tasks=$(aws ecs list-tasks --cluster ivs | jq '.taskArns' | sed -n -e 's/^.*\///p' | sed 's/"//' | sed -e 'N;s/\n//;s/,/ /')
185 |
186 | print_progress
187 |
188 | # Waits the task stop
189 | aws ecs wait tasks-stopped --cluster ivs --tasks $tasks
190 |
191 | # Deletes de ivs-webrtc service
192 | ecs_service_delete
193 |
194 | if [ ${ivs_task_definition[0]} ]
195 | then
196 | # De-register task definition ivs-webrtc
197 | deregister_task_definition
198 | fi
199 |
200 | # Deletes the ivs cluster
201 | ecs_cluster_delete
202 |
203 |
204 | else
205 | # Deletes de ivs-webrtc service
206 | ecs_service_delete
207 |
208 | if [ ${ivs_task_definition[0]} ]
209 | then
210 | # De-register task definition ivs-webrtc
211 | deregister_task_definition
212 | fi
213 |
214 | # Deletes the ivs cluster
215 | ecs_cluster_delete
216 |
217 | fi
218 |
219 | }
220 |
221 | ################################################################################
222 | # Security Group Deprovisioning #
223 | ################################################################################
224 |
225 | function security_group_deprov () {
226 |
227 | # This function deletes the security group ivs-sg
228 |
229 | # # Test if the security group exists and save the result on security_group variable
230 | aws ec2 describe-security-groups --group-name ivs-sg > /dev/null 2>&1 && security_group='OK' || security_group='NOK'
231 |
232 | if [ $security_group = 'OK' ]
233 | then
234 |
235 | echo -e "${GREEN}Deleting security group ivs-sg...${NC}"
236 | print_progress
237 | aws ec2 delete-security-group --group-name ivs-sg
238 |
239 | else
240 |
241 | echo -e "${RED}It seems you have already deleted the security group ivs-sg!!!${NC}"
242 |
243 | fi
244 | }
245 |
246 | ################################################################################
247 | # Lambda Deprovisioning #
248 | ################################################################################
249 |
250 | function lambda_deprov () {
251 |
252 | # This function deletes the Lambda function ivs-ip-register
253 |
254 | # Removes the lambda function ivs-ip-register
255 | echo -e "${GREEN}Deleting Lambda function ivs-ip-register...${NC}"
256 | aws lambda delete-function --function-name ivs-ip-register > /dev/null 2>&1 && echo -e "${GREEN}Lambda function ivs-ip-register deleted${NC}" \
257 | || echo -e "${RED}It seems you have already deleted the Lambda function ivs-ip-register!!!${NC}"
258 |
259 | }
260 |
261 | ################################################################################
262 | # IAM Deprovisioning #
263 | ################################################################################
264 |
265 | function detach_policy () {
266 |
267 | # This function detaches policies from iam roles
268 |
269 | echo -e "${GREEN}Detaching policies...${NC}"
270 |
271 | # Iterate over the arguments and detaches policy by policy
272 | for i in ${@}
273 | do
274 |
275 | # Go to next register case file does not exist
276 | if [ $i = 'ivs-webrtc-codebuild-role' ] || [ $i = 'ivs-lambda-role' ] || [ $i = 'ivs-ecs-execution-role' ]
277 | then
278 |
279 | continue
280 |
281 | fi
282 |
283 | p_name=$(echo -e ${i} | sed 's:.*/::')
284 |
285 | # Detaches policies
286 | aws iam detach-role-policy --role-name ${@: -1} --policy-arn ${i} \
287 | > /dev/null 2>&1 && echo -e "${GREEN}Policy ${p_name} detached${NC}" \
288 | || echo -e "${RED}It seems that ${p_name} was already detached!!!${NC}"
289 |
290 | done
291 | }
292 |
293 | function delete_policy () {
294 |
295 | # This function deletes policies
296 |
297 | echo -e "${GREEN}Deleting policies...${NC}"
298 |
299 | # Iterate over the arguments and detaches policy by policy
300 | for i in ${@}
301 | do
302 |
303 | # Go to next register case file does not exist
304 | if [ $i = 'ivs-webrtc-codebuild-role' ] || [ $i = 'ivs-lambda-role' ] || [ $i = 'ivs-ecs-execution-role' ]
305 | then
306 |
307 | continue
308 |
309 | fi
310 |
311 | # Captures the name of the policies
312 | r_name=$(echo -e ${i} | sed 's:.*/::')
313 |
314 | # Deletes policies
315 | aws iam delete-policy --policy-arn ${i} > /dev/null 2>&1 \
316 | && echo -e "${GREEN}Policy ${r_name} deleted${NC}" \
317 | || echo -e "${RED}It seems that ${r_name} was already deleted!!!${NC}"
318 |
319 | done
320 |
321 | }
322 |
323 | function delete_role () {
324 |
325 | # This function deletes roles
326 |
327 | echo -e "${GREEN}Deleting roles...${NC}"
328 |
329 | # Iterate over the arguments and deletes role by role
330 | for i in ${@}
331 | do
332 |
333 | # Go to next register case file does not exist
334 |
335 | aws iam delete-role --role-name ${i} > /dev/null 2>&1 \
336 | && echo -e "${GREEN}Role ${i} deleted${NC}" \
337 | || echo -e "${RED}It seems that ${i} role was already deleted!!!${NC}"
338 |
339 | done
340 |
341 | }
342 |
343 | function iam_deprov () {
344 |
345 | # This function will delete all IAM roles and policies created to ivs-webrtc project
346 |
347 | # Array that stores all policies arn that should be detached
348 | policies_lambda=($(cat ./temp_files/lambda_policy_arn.txt) "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" \
349 | "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess")
350 |
351 | policies_ecs=("arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" "arn:aws:iam::aws:policy/AWSOpsWorksCloudWatchLogs")
352 |
353 | # Array that stores all policies arn that should be deleted
354 | policies_to_delete=($(cat ./temp_files/lambda_policy_arn.txt))
355 |
356 | # Array that stores all roles
357 | roles_to_delete=('ivs-ecs-execution-role' 'ivs-lambda-role')
358 |
359 | # Calls detach_policy passing the policies array as argument
360 | detach_policy ${policies_lambda[@]} ivs-lambda-role
361 |
362 | # Calls detach_policy passing the policies array as argument
363 | detach_policy ${policies_ecs[@]} ivs-ecs-execution-role
364 |
365 | # Calls delete_policy passing the policies array as argument
366 | delete_policy ${policies_to_delete[@]}
367 |
368 | # Calls delete_role passing the policies array as argument
369 | delete_role ${roles_to_delete[@]}
370 |
371 | unset iam_role
372 |
373 | }
374 |
375 | ################################################################################
376 | # CloudWatch Log Group Deprovisioning #
377 | ################################################################################
378 |
379 | function cloudwatchlogs_deprov () {
380 |
381 | # This function will delete the cloudwatch log group /ecs/ivs-webrtc
382 |
383 | # Removes /ecs/ivs-webrtc log group
384 | echo -e "${GREEN}Deleting cloudwatch log groups...${NC}"
385 | aws logs delete-log-group --log-group-name /ecs/ivs-webrtc > /dev/null 2>&1 \
386 | && echo -e "${GREEN}Cloudwatch log group /ecs/ivs-webrtc deleted.${NC}" \
387 | || echo -e "${RED}It seems you have already deleted the /ecs/ivs-webrtc log group!!!${NC}"
388 |
389 | # Removes /aws/codebuild/ivs-webrtc log group
390 | aws logs delete-log-group --log-group-name /aws/codebuild/ivs-webrtc > /dev/null 2>&1 \
391 | && echo -e "${GREEN}Cloudwatch log group /aws/codebuild/ivs-webrtc deleted.${NC}" \
392 | || echo -e "${RED}It seems you have already deleted the /aws/codebuild/ivs-webrtc log group!!!${NC}"
393 |
394 | # Removes /aws/lambda/ivs-ip-register log group
395 | aws logs delete-log-group --log-group-name /aws/lambda/ivs-ip-register > /dev/null 2>&1 \
396 | && echo -e "${GREEN}Cloudwatch log group /aws/lambda/ivs-ip-register deleted.${NC}" \
397 | || echo -e "${RED}It seems you have already deleted the /aws/lambda/ivs-ip-register log group!!!${NC}"
398 |
399 | }
400 |
401 | ################################################################################
402 | # Clean Temporary Files #
403 | ################################################################################
404 |
405 | function delete_temp_files () {
406 |
407 | # This function will delete all temporary files that was created by ivs-webrtc during the deployment
408 |
409 | files=('./json_configs/ivs_events_rule.json' './json_configs/ivs_ecs_service.json' './temp_files/my_subnets.txt' './json_configs/ivs_task_definition.json' \
410 | './temp_files/ecs_cluster_arn.txt' './temp_files/ivs_sg.txt' './json_configs/lambda.json' './temp_files/ivs_ecs_execution_role_arn.txt' \
411 | './temp_files/lambda_policy_arn.txt' './temp_files/ivs_lambda_function_arn.txt' './temp_files/ivs_event_rule_arn.txt' './temp_files/ecr_repository.txt' \
412 | './temp_files/s3_bucket.txt' 'lambda.zip' './temp_files/ecr_repository.txt' './temp_files/s3_bucket.txt' './json_configs/ivs_codebuild_base_policy.json' \
413 | './json_configs/ivs_codebuild_s3_policy.json' './json_configs/ivs_codebuild_log_policy.json' './json_configs/ivs_codebuild.json' './temp_files/codebuild_subnets.txt' \
414 | './json_configs/ivs_codebuild_vpc_policy.json' './temp_files/ivs_webrtc_codebuild_role_arn.txt' './temp_files/ivs_codebuild_ecr_arn.txt' './temp_files/ivs_codebuild_base_arn.txt' \
415 | './temp_files/ivs_codebuild_vpc_arn.txt' './temp_files/ivs_codebuild_s3_arn.txt' './temp_files/ivs_codebuild_log_arn.txt' './temp_files/ivs_codebuild_build.json' \
416 | './temp_files/ecr_uri.txt' './temp_files/accountid.txt' './docker_files/docker.zip' ./temp_files/ivs_codebuild_arn.txt)
417 |
418 | delete_file ${files[@]}
419 |
420 | }
421 |
422 | ################################################################################
423 | # Deletes Fargate Service #
424 | ################################################################################
425 |
426 | function ecs_service_delete () {
427 |
428 | # This function deletes the ecs ivs-webrtc service
429 |
430 | if [ $ivs_cluster = 'ACTIVE' ] && [ $service_status = 'ACTIVE' ]
431 | then
432 |
433 | echo -e "${GREEN}Deleting service ivs-webrtc...${NC}"
434 | aws ecs delete-service --cluster ivs --service ivs-webrtc >> /dev/null
435 | else
436 |
437 | echo -e "${RED}It seems you have already deleted the ivs-webrtc service!!!${NC}"
438 |
439 | fi
440 |
441 | }
442 |
443 | ################################################################################
444 | # Delete Files #
445 | ################################################################################
446 |
447 |
448 | function delete_file () {
449 |
450 | # This function deletes the temporary files
451 |
452 | # Runs delete to each file present in the array
453 | for i in ${@}
454 | do
455 |
456 | echo -e "${GREEN}Deleting ${i}...${NC}"
457 | rm ${i} > /dev/null 2>&1 && echo -e "${GREEN}File ${i} deleted.${NC}" \
458 | || echo -e "${RED}File ${i} does not exist${NC}"
459 | done
460 |
461 | }
462 |
463 |
464 | ################################################################################
465 | # Delete ECS Cluster #
466 | ################################################################################
467 |
468 | function ecs_cluster_delete () {
469 |
470 | # This function deletes the ecs ivs-webrtc service
471 | if [ $ivs_cluster = 'ACTIVE' ]
472 | then
473 |
474 | echo -e "${GREEN}Deleting cluster ivs...${NC}"
475 | aws ecs delete-cluster --cluster ivs >> /dev/null
476 | else
477 |
478 | echo -e "${RED}It seems you have already deleted the ivs cluster!!!${NC}"
479 |
480 | fi
481 |
482 | }
483 |
484 | ################################################################################
485 | # Deletes ECS Task Definition #
486 | ################################################################################
487 |
488 | function deregister_task_definition () {
489 |
490 |
491 | echo -e "${GREEN}Deregistering task definition ivs-webrtc...${NC}"
492 | for t in ${ivs_task_definition[@]};
493 | do
494 | aws ecs deregister-task-definition --task-definition ${t} >> /dev/null
495 | done
496 | }
497 |
498 | ################################################################################
499 | # Error handling #
500 | ################################################################################
501 |
502 | function error_exit () {
503 |
504 | # This function is used for error handling
505 |
506 | echo -e "${RED}$1${NC}" 1>&2
507 | exit 1
508 | }
509 |
510 | ################################################################################
511 | # Progress bar #
512 | ################################################################################
513 |
514 | function print_progress () {
515 |
516 | # Print # on screen
517 |
518 | for i in {0..7}
519 | do
520 | sleep 2
521 | echo -n "#"
522 |
523 | done
524 |
525 | }
526 |
527 | ################################################################################
528 | # Cleaning Options #
529 | ################################################################################
530 |
531 | function clean () {
532 |
533 | # This function will receive a argument from the command line to start the cleaning process
534 | # It will be possible to uninstall everything or just pontual services
535 |
536 | option=$1
537 |
538 | case $option in
539 | all)
540 | echo -e "${GREEN}###Preparing to clean aws resources deployed by ivs-webrtc...###${NC}"
541 | codebuild_deprov || { error_exit 'Failed while removing codebuild project!'; }
542 | codebuild_rp_deprov || { error_exit 'Failed while removing codebuild roles and policies!'; }
543 | s3_deprov || { error_exit 'Failed while removing s3 bucket!'; }
544 | ecr_deprov || { error_exit 'Failed while removing ecr repository!'; }
545 | eventbridge_deprov || { error_exit 'Failed while removing eventbridge rules and triggers!'; }
546 | fargate_deprov || { error_exit 'Failed while removing Fargate ECS resources!'; }
547 | security_group_deprov || { error_exit 'Failed while removing security groups'; }
548 | #dynamodb_deprov || { error_exit 'dynamodb table deployment failed!'; }
549 | iam_deprov || { error_exit 'Failed while removing iam roles and policies'; }
550 | lambda_deprov || { error_exit 'Failed while removing lambda resources'; }
551 | cloudwatchlogs_deprov || { error_exit 'Failed while removing cloudwatch log group!'; }
552 | #delete_temp_files || { error_exit 'Failed while removing temporary files!'; }
553 | echo -e "${YELLOW}If you dont see any errors, run the command below to delete the temporary files${NC}"
554 | echo -e "${GREEN}./uninstall_ivs_backend.sh clean files${NC}"
555 | ;;
556 |
557 | codebuild)
558 | echo -e "${GREEN}###Cleaning codebuild ivs-webrtc project###${NC}"
559 | codebuild_deprov
560 | ;;
561 |
562 | codebuild_rp)
563 | echo -e "${GREEN}###Cleaning codebuild roles and policies###${NC}"
564 | codebuild_rp_deprov
565 | ;;
566 | s3)
567 | echo -e "${GREEN}###Cleaning S3 resources###${NC}"
568 | s3_deprov
569 | ;;
570 | ecr)
571 | echo -e "${GREEN}###Cleaning ECR resources###${NC}"
572 | ecr_deprov
573 | ;;
574 | events)
575 | echo -e "${GREEN}###Cleaning eventbridge ivs-webrtc resources###${NC}"
576 | eventbridge_deprov
577 | ;;
578 |
579 | fargate)
580 | echo -e "${GREEN}###Cleaning fargate ivs-webrtc resources###${NC}"
581 | fargate_deprov
582 | ;;
583 |
584 | sg)
585 | echo -e "${GREEN}###Cleaning security group ivs-webrtc resources###${NC}"
586 | security_group_deprov
587 | ;;
588 |
589 | dynamodb)
590 | echo -e "${GREEN}###Cleaning dybamodb ivs-webrtc resources###${NC}"
591 | dynamodb_deprov
592 | ;;
593 |
594 | lambda)
595 | echo -e "${GREEN}###Cleaning lambda ivs-webrtc resources###${NC}"
596 | lambda_deprov
597 | ;;
598 |
599 | iam)
600 | echo -e "${GREEN}###Cleaning iam ivs-webrtc resources###${NC}"
601 | iam_deprov
602 | ;;
603 |
604 | logs)
605 | echo -e "${GREEN}###Cleaning cloudwatchlogs ivs-webrtc resources###${NC}"
606 | cloudwatchlogs_deprov
607 | ;;
608 |
609 | files)
610 | echo -e "${GREEN}###Cleaning ivs-webrtc temporary files###${NC}"
611 | delete_temp_files
612 | ;;
613 |
614 | *)
615 | echo -e "${RED}This option is not valid!${NC}"
616 | ;;
617 | esac
618 |
619 | }
620 |
621 | # Enables script arguments
622 | "$@"
623 |
624 |
625 | # Prints help in case of calling the script without arguments
626 | if [ -z $1 ]
627 | then
628 |
629 | help
630 | fi
--------------------------------------------------------------------------------
/doc/IVSCopy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/IVSCopy.png
--------------------------------------------------------------------------------
/doc/IVSCreateChannel_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/IVSCreateChannel_1.png
--------------------------------------------------------------------------------
/doc/IVSCreateChannel_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/IVSCreateChannel_2.png
--------------------------------------------------------------------------------
/doc/app01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/app01.png
--------------------------------------------------------------------------------
/doc/arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/arch.png
--------------------------------------------------------------------------------
/doc/auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/auth.png
--------------------------------------------------------------------------------
/doc/auth01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/auth01.png
--------------------------------------------------------------------------------
/doc/codearr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/codearr.png
--------------------------------------------------------------------------------
/doc/coderemote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/coderemote.png
--------------------------------------------------------------------------------
/doc/codevideo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/codevideo.png
--------------------------------------------------------------------------------
/doc/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/config.png
--------------------------------------------------------------------------------
/doc/front.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/front.png
--------------------------------------------------------------------------------
/doc/golive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/golive.png
--------------------------------------------------------------------------------
/doc/saveivs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/saveivs.png
--------------------------------------------------------------------------------
/doc/sslerror.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/sslerror.png
--------------------------------------------------------------------------------
/doc/videoworkflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/doc/videoworkflow.png
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 |
2 |
3 | #amplify-do-not-edit-begin
4 | amplify/\#current-cloud-backend
5 | amplify/.config/local-*
6 | amplify/logs
7 | amplify/mock-data
8 | amplify/backend/amplify-meta.json
9 | amplify/backend/.temp
10 | build/
11 | dist/
12 | node_modules/
13 | aws-exports.js
14 | awsconfiguration.json
15 | amplifyconfiguration.json
16 | amplifyconfiguration.dart
17 | amplify-build-config.json
18 | amplify-gradle-config.json
19 | amplifytools.xcconfig
20 | .secret-*
21 | **.sample
22 | #amplify-do-not-edit-end
23 |
--------------------------------------------------------------------------------
/frontend/BROWSER.md:
--------------------------------------------------------------------------------
1 | # Simplifying live streaming contribution - Customize the APP
2 |
3 | ## Use the video MediaDevices.getUserMedia() instead of the Canvas element
4 |
5 | Due to browser compatibility, the video track has been wrapped into a canvas element, but it can impact on the video quality, if Chrome is the preferable browser, you can capture the stream using the video html component instead.
6 |
7 | You should change the file frontend/src/components/HomePage.js
8 | The affected function is handleCameraReady()
9 |
10 | Currently its using the element canvasVideo, change to use the video, instead as per bellow.
11 |
12 |
13 |
14 | You also need to change the function function startStream, to avoid audio duplication.
15 | Remove the audioStreaming from the Array in the forEach function.
16 |
17 |
18 |
19 | ## Change local app to stream to remote container
20 |
21 | The app is currently configure to take in consideration the protocol used, and if it's http, then set the local host as the endpoint. You can replace the testServer by the server, to stream to the remote server from your local machine.
22 |
23 |
24 |
25 | ## Additional information:
26 |
27 | * [HTMLCanvasElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement)
28 | * [HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement)
29 |
30 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Simplifying live streaming contribution - run locally.
2 |
3 | This sample demo app, captures the video and use a proxy in node.js to transwrap the stream to Amazon IVS
4 |
5 | ## Deployment Steps:
6 |
7 | For building the integration with AWS components and host our web application, we will be using AWS Amplify. For more complete steps on installing and configuring AWS Amplify, please visit the [Amplify Documentation for React](https://docs.amplify.aws/start/getting-started/installation/q/integration/react#option-2-follow-the-instructions).
8 |
9 | ## Pre-requirements
10 |
11 | ```sh
12 | npm install -g @aws-amplify/cli
13 | amplify configure
14 | ```
15 |
16 | For a more complete guide on how to get started with amplify CLI, please follow the instructions of the official [AWS Amplify Documentation](https://docs.amplify.aws/cli/start/install/#install-the-amplify-cli).
17 |
18 | ### Frontend and APIs: WebRTC video capture:
19 | version 2 preview
20 |
21 | ```sh
22 | git clone --branch v2preview https://github.com/aws-samples/aws-simple-streaming-webapp.git
23 | cd aws-simple-streaming-webpp/frontend/
24 | npm install
25 | amplify init
26 | ```
27 |
28 | As name for the environment, please select *dev*
29 |
30 | ```sh
31 | ### Configuration details
32 | ? Enter a name for the environment dev
33 | ? Choose your default editor: Visual Studio Code
34 | Using default provider awscloudformation
35 | ? Select the authentication method you want to use: AWS profile
36 | ```
37 |
38 | With amplify init, the project and resources will be initialized in the AWS Cloud environment.
39 | Now you can push, with amplify push to create the resources.
40 |
41 | *Note:* This Command will deploy the following resources in your account:
42 | * API Gateway: Save and retrieve IVS Parameters and ECS Container availability information
43 | * DynamoDB: Store IVS and Container servers parameters
44 | * Lambda Functions: For checking stored parameters and check Event Bridge information
45 | To list the resources that will be created you can use the command ```amplify status```
46 |
47 | ```sh
48 | amplify push
49 | ```
50 |
51 | ### Run the solution in your local environment
52 |
53 | ```sh
54 | npm start
55 | ```
56 |
57 | ### Create your user and password
58 |
59 | It should load the authentication page. Now you can create your first account and sign in.
60 |
61 |
62 | ### Add the ingestEndpoint, streamKey value and playbackUrl to the interface
63 | **[OPTIONAL] If you don't have a Amazon IVS created, you can follow [this steps](link).**
64 | 4.2. In your local environment http://127.0.0.1:3000 (http://127.0.0.1:3000/) the following application will be loaded.
65 |
66 |
67 |
68 | ### Start the server in a local environment: localhost
69 |
70 | ```sh
71 | cd backend/
72 | npm install
73 | npm run startDev
74 | ```
75 | **Note:** if you are running locally in HTTPS, the socket needs to be HTTPS as well, [please follow this instruction instead.](../backend/README_HTTPS.md)
76 |
77 | ### Test your streaming directly from your browser
78 |
79 |
80 |
81 | ### [Optional]: Cleanup, removing the provisioned AWS resources.
82 |
83 | ```sh
84 | cd ../frontend
85 | amplify delete
86 | ```
87 |
88 | ## Choose your next adventure:
89 | * [**Backend: Transwraping Amazon ECS container:**](/backend/README.md)Install the remote video transwrap server.
90 | * [**Create a IVS Channel using a shell script**](/backend/CREATEIVS.md)
91 | * [**Customize the web app and compatibility discussions**](BROWSER.md)
92 |
--------------------------------------------------------------------------------
/frontend/amplify/.config/project-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "simplewebstreaming",
3 | "version": "3.1",
4 | "frontend": "javascript",
5 | "javascript": {
6 | "framework": "react",
7 | "config": {
8 | "SourceDir": "src",
9 | "DistributionDir": "build",
10 | "BuildCommand": "npm run-script build",
11 | "StartCommand": "npm run-script start"
12 | }
13 | },
14 | "providers": [
15 | "awscloudformation"
16 | ]
17 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/api/APIGatewayAuthStack.json:
--------------------------------------------------------------------------------
1 | {
2 | "Description": "API Gateway policy stack created using Amplify CLI",
3 | "AWSTemplateFormatVersion": "2010-09-09",
4 | "Parameters": {
5 | "authRoleName": {
6 | "Type": "String"
7 | },
8 | "unauthRoleName": {
9 | "Type": "String"
10 | },
11 | "env": {
12 | "Type": "String"
13 | },
14 | "saveIVSparam": {
15 | "Type": "String"
16 | }
17 | },
18 | "Conditions": {
19 | "ShouldNotCreateEnvResources": {
20 | "Fn::Equals": [
21 | {
22 | "Ref": "env"
23 | },
24 | "NONE"
25 | ]
26 | }
27 | },
28 | "Resources": {
29 | "PolicyAPIGWAuth1": {
30 | "Type": "AWS::IAM::ManagedPolicy",
31 | "Properties": {
32 | "PolicyDocument": {
33 | "Version": "2012-10-17",
34 | "Statement": [
35 | {
36 | "Effect": "Allow",
37 | "Action": [
38 | "execute-api:Invoke"
39 | ],
40 | "Resource": [
41 | {
42 | "Fn::Join": [
43 | "",
44 | [
45 | "arn:aws:execute-api:",
46 | {
47 | "Ref": "AWS::Region"
48 | },
49 | ":",
50 | {
51 | "Ref": "AWS::AccountId"
52 | },
53 | ":",
54 | {
55 | "Ref": "saveIVSparam"
56 | },
57 | "/",
58 | {
59 | "Fn::If": [
60 | "ShouldNotCreateEnvResources",
61 | "Prod",
62 | {
63 | "Ref": "env"
64 | }
65 | ]
66 | },
67 | "/*/putitens/*"
68 | ]
69 | ]
70 | },
71 | {
72 | "Fn::Join": [
73 | "",
74 | [
75 | "arn:aws:execute-api:",
76 | {
77 | "Ref": "AWS::Region"
78 | },
79 | ":",
80 | {
81 | "Ref": "AWS::AccountId"
82 | },
83 | ":",
84 | {
85 | "Ref": "saveIVSparam"
86 | },
87 | "/",
88 | {
89 | "Fn::If": [
90 | "ShouldNotCreateEnvResources",
91 | "Prod",
92 | {
93 | "Ref": "env"
94 | }
95 | ]
96 | },
97 | "/*/putitens"
98 | ]
99 | ]
100 | },
101 | {
102 | "Fn::Join": [
103 | "",
104 | [
105 | "arn:aws:execute-api:",
106 | {
107 | "Ref": "AWS::Region"
108 | },
109 | ":",
110 | {
111 | "Ref": "AWS::AccountId"
112 | },
113 | ":",
114 | {
115 | "Ref": "saveIVSparam"
116 | },
117 | "/",
118 | {
119 | "Fn::If": [
120 | "ShouldNotCreateEnvResources",
121 | "Prod",
122 | {
123 | "Ref": "env"
124 | }
125 | ]
126 | },
127 | "/*/getServers/*"
128 | ]
129 | ]
130 | },
131 | {
132 | "Fn::Join": [
133 | "",
134 | [
135 | "arn:aws:execute-api:",
136 | {
137 | "Ref": "AWS::Region"
138 | },
139 | ":",
140 | {
141 | "Ref": "AWS::AccountId"
142 | },
143 | ":",
144 | {
145 | "Ref": "saveIVSparam"
146 | },
147 | "/",
148 | {
149 | "Fn::If": [
150 | "ShouldNotCreateEnvResources",
151 | "Prod",
152 | {
153 | "Ref": "env"
154 | }
155 | ]
156 | },
157 | "/*/getServers"
158 | ]
159 | ]
160 | }
161 | ]
162 | }
163 | ]
164 | },
165 | "Roles": [
166 | {
167 | "Ref": "authRoleName"
168 | }
169 | ]
170 | }
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/api/saveIVSparam/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "paths": {
4 | "/putitens": {
5 | "name": "/putitens",
6 | "lambdaFunction": "saveIVSparam",
7 | "permissions": {
8 | "setting": "private",
9 | "auth": [
10 | "create",
11 | "read",
12 | "update",
13 | "delete"
14 | ]
15 | }
16 | },
17 | "/getServers": {
18 | "name": "/getServers",
19 | "lambdaFunction": "ISSgetServers",
20 | "permissions": {
21 | "setting": "private",
22 | "auth": [
23 | "create",
24 | "read",
25 | "update",
26 | "delete"
27 | ]
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/auth/simplewebstreaming178ae288/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "cognitoConfig": {
4 | "identityPoolName": "simplewebstreaming178ae288_identitypool_178ae288",
5 | "allowUnauthenticatedIdentities": false,
6 | "resourceNameTruncated": "simple178ae288",
7 | "userPoolName": "simplewebstreaming178ae288_userpool_178ae288",
8 | "autoVerifiedAttributes": [
9 | "email"
10 | ],
11 | "mfaConfiguration": "OFF",
12 | "mfaTypes": [
13 | "SMS Text Message"
14 | ],
15 | "smsAuthenticationMessage": "Your authentication code is {####}",
16 | "smsVerificationMessage": "Your verification code is {####}",
17 | "emailVerificationSubject": "Your verification code",
18 | "emailVerificationMessage": "Your verification code is {####}",
19 | "defaultPasswordPolicy": false,
20 | "passwordPolicyMinLength": 8,
21 | "passwordPolicyCharacters": [],
22 | "requiredAttributes": [
23 | "email"
24 | ],
25 | "aliasAttributes": [],
26 | "userpoolClientGenerateSecret": false,
27 | "userpoolClientRefreshTokenValidity": 30,
28 | "userpoolClientWriteAttributes": [
29 | "email"
30 | ],
31 | "userpoolClientReadAttributes": [
32 | "email"
33 | ],
34 | "userpoolClientLambdaRole": "simple178ae288_userpoolclient_lambda_role",
35 | "userpoolClientSetAttributes": false,
36 | "sharedId": "178ae288",
37 | "resourceName": "simplewebstreaming178ae288",
38 | "authSelections": "identityPoolAndUserPool",
39 | "useDefault": "default",
40 | "usernameAttributes": [
41 | "email"
42 | ],
43 | "userPoolGroupList": [],
44 | "serviceName": "Cognito",
45 | "usernameCaseSensitive": false,
46 | "useEnabledMfas": true
47 | }
48 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/backend-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "function": {
3 | "saveIVSparam": {
4 | "build": true,
5 | "providerPlugin": "awscloudformation",
6 | "service": "Lambda",
7 | "dependsOn": [
8 | {
9 | "category": "storage",
10 | "resourceName": "IVSparam",
11 | "attributes": [
12 | "Name",
13 | "Arn"
14 | ],
15 | "attributeEnvMap": {
16 | "Name": "TABLE_NAME",
17 | "Arn": "TABLE_ARN"
18 | }
19 | }
20 | ]
21 | },
22 | "ISSgetServers": {
23 | "build": true,
24 | "providerPlugin": "awscloudformation",
25 | "service": "Lambda",
26 | "dependsOn": [
27 | {
28 | "category": "storage",
29 | "resourceName": "ISStaskdnstrack",
30 | "attributes": [
31 | "Name",
32 | "Arn"
33 | ],
34 | "attributeEnvMap": {
35 | "Name": "TABLE_NAME",
36 | "Arn": "TABLE_ARN"
37 | }
38 | }
39 | ]
40 | }
41 | },
42 | "auth": {
43 | "simplewebstreaming178ae288": {
44 | "service": "Cognito",
45 | "providerPlugin": "awscloudformation",
46 | "dependsOn": [],
47 | "customAuth": false,
48 | "frontendAuthConfig": {
49 | "socialProviders": [],
50 | "usernameAttributes": [
51 | "EMAIL"
52 | ],
53 | "signupAttributes": [
54 | "EMAIL"
55 | ],
56 | "passwordProtectionSettings": {
57 | "passwordPolicyMinLength": 8,
58 | "passwordPolicyCharacters": []
59 | },
60 | "mfaConfiguration": "OFF",
61 | "mfaTypes": [
62 | "SMS"
63 | ],
64 | "verificationMechanisms": [
65 | "EMAIL"
66 | ]
67 | }
68 | }
69 | },
70 | "storage": {
71 | "IVSparam": {
72 | "service": "DynamoDB",
73 | "providerPlugin": "awscloudformation"
74 | },
75 | "ISStaskdnstrack": {
76 | "service": "DynamoDB",
77 | "providerPlugin": "awscloudformation"
78 | }
79 | },
80 | "api": {
81 | "saveIVSparam": {
82 | "service": "API Gateway",
83 | "providerPlugin": "awscloudformation",
84 | "dependsOn": [
85 | {
86 | "category": "function",
87 | "resourceName": "saveIVSparam",
88 | "attributes": [
89 | "Name",
90 | "Arn"
91 | ]
92 | },
93 | {
94 | "category": "function",
95 | "resourceName": "ISSgetServers",
96 | "attributes": [
97 | "Name",
98 | "Arn"
99 | ]
100 | }
101 | ]
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/ISSgetServers-cloudformation-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"8.3.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
4 | "Parameters": {
5 | "CloudWatchRule": {
6 | "Type": "String",
7 | "Default": "NONE",
8 | "Description": " Schedule Expression"
9 | },
10 | "deploymentBucketName": {
11 | "Type": "String"
12 | },
13 | "env": {
14 | "Type": "String"
15 | },
16 | "s3Key": {
17 | "Type": "String"
18 | },
19 | "storageISStaskdnstrackName": {
20 | "Type": "String",
21 | "Default": "storageISStaskdnstrackName"
22 | },
23 | "storageISStaskdnstrackArn": {
24 | "Type": "String",
25 | "Default": "storageISStaskdnstrackArn"
26 | }
27 | },
28 | "Conditions": {
29 | "ShouldNotCreateEnvResources": {
30 | "Fn::Equals": [
31 | {
32 | "Ref": "env"
33 | },
34 | "NONE"
35 | ]
36 | }
37 | },
38 | "Resources": {
39 | "LambdaFunction": {
40 | "Type": "AWS::Lambda::Function",
41 | "Metadata": {
42 | "aws:asset:path": "./src",
43 | "aws:asset:property": "Code"
44 | },
45 | "Properties": {
46 | "Code": {
47 | "S3Bucket": {
48 | "Ref": "deploymentBucketName"
49 | },
50 | "S3Key": {
51 | "Ref": "s3Key"
52 | }
53 | },
54 | "Handler": "index.handler",
55 | "FunctionName": {
56 | "Fn::If": [
57 | "ShouldNotCreateEnvResources",
58 | "ISSgetServers",
59 | {
60 | "Fn::Join": [
61 | "",
62 | [
63 | "ISSgetServers",
64 | "-",
65 | {
66 | "Ref": "env"
67 | }
68 | ]
69 | ]
70 | }
71 | ]
72 | },
73 | "Environment": {
74 | "Variables": {
75 | "ENV": {
76 | "Ref": "env"
77 | },
78 | "REGION": {
79 | "Ref": "AWS::Region"
80 | }
81 | }
82 | },
83 | "Role": {
84 | "Fn::GetAtt": [
85 | "LambdaExecutionRole",
86 | "Arn"
87 | ]
88 | },
89 | "Runtime": "nodejs14.x",
90 | "Layers": [],
91 | "Timeout": 25
92 | }
93 | },
94 | "LambdaExecutionRole": {
95 | "Type": "AWS::IAM::Role",
96 | "Properties": {
97 | "RoleName": {
98 | "Fn::If": [
99 | "ShouldNotCreateEnvResources",
100 | "simplewebstreamingLambdaRole1b3066e3",
101 | {
102 | "Fn::Join": [
103 | "",
104 | [
105 | "simplewebstreamingLambdaRole1b3066e3",
106 | "-",
107 | {
108 | "Ref": "env"
109 | }
110 | ]
111 | ]
112 | }
113 | ]
114 | },
115 | "AssumeRolePolicyDocument": {
116 | "Version": "2012-10-17",
117 | "Statement": [
118 | {
119 | "Effect": "Allow",
120 | "Principal": {
121 | "Service": [
122 | "lambda.amazonaws.com"
123 | ]
124 | },
125 | "Action": [
126 | "sts:AssumeRole"
127 | ]
128 | }
129 | ]
130 | }
131 | }
132 | },
133 | "lambdaexecutionpolicy": {
134 | "DependsOn": [
135 | "LambdaExecutionRole"
136 | ],
137 | "Type": "AWS::IAM::Policy",
138 | "Properties": {
139 | "PolicyName": "lambda-execution-policy",
140 | "Roles": [
141 | {
142 | "Ref": "LambdaExecutionRole"
143 | }
144 | ],
145 | "PolicyDocument": {
146 | "Version": "2012-10-17",
147 | "Statement": [
148 | {
149 | "Effect": "Allow",
150 | "Action": [
151 | "logs:CreateLogGroup",
152 | "logs:CreateLogStream",
153 | "logs:PutLogEvents"
154 | ],
155 | "Resource": {
156 | "Fn::Sub": [
157 | "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
158 | {
159 | "region": {
160 | "Ref": "AWS::Region"
161 | },
162 | "account": {
163 | "Ref": "AWS::AccountId"
164 | },
165 | "lambda": {
166 | "Ref": "LambdaFunction"
167 | }
168 | }
169 | ]
170 | }
171 | },
172 | {
173 | "Effect": "Allow",
174 | "Action": [
175 | "dynamodb:DescribeTable",
176 | "dynamodb:GetItem",
177 | "dynamodb:Query",
178 | "dynamodb:Scan",
179 | "dynamodb:PutItem",
180 | "dynamodb:UpdateItem",
181 | "dynamodb:DeleteItem"
182 | ],
183 | "Resource": [
184 | {
185 | "Ref": "storageISStaskdnstrackArn"
186 | },
187 | {
188 | "Fn::Join": [
189 | "/",
190 | [
191 | {
192 | "Ref": "storageISStaskdnstrackArn"
193 | },
194 | "index/*"
195 | ]
196 | ]
197 | }
198 | ]
199 | }
200 | ]
201 | }
202 | }
203 | }
204 | },
205 | "Outputs": {
206 | "Name": {
207 | "Value": {
208 | "Ref": "LambdaFunction"
209 | }
210 | },
211 | "Arn": {
212 | "Value": {
213 | "Fn::GetAtt": [
214 | "LambdaFunction",
215 | "Arn"
216 | ]
217 | }
218 | },
219 | "Region": {
220 | "Value": {
221 | "Ref": "AWS::Region"
222 | }
223 | },
224 | "LambdaExecutionRole": {
225 | "Value": {
226 | "Ref": "LambdaExecutionRole"
227 | }
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/amplify.state:
--------------------------------------------------------------------------------
1 | {
2 | "pluginId": "amplify-nodejs-function-runtime-provider",
3 | "functionRuntime": "nodejs",
4 | "useLegacyBuild": true,
5 | "defaultEditorFile": "src/app.js"
6 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/custom-policies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Action": [],
4 | "Resource": []
5 | }
6 | ]
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/function-parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "lambdaLayers": []
3 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/src/event.json:
--------------------------------------------------------------------------------
1 | {
2 | "httpMethod": "POST",
3 | "path": "/item",
4 | "queryStringParameters": {
5 | "limit": "10"
6 | },
7 | "headers": {
8 | "Content-Type": "application/json"
9 | },
10 | "body": "{\"msg\":\"Hello from the event.json body\"}"
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/ISSgetServers/src/index.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 | console.log("Loading Event");
4 | // aws sdk loading
5 | const AWS = require("aws-sdk");
6 | const dynamodb = new AWS.DynamoDB.DocumentClient();
7 |
8 | exports.handler = async (event) => {
9 | const headers = {
10 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
11 | "Access-Control-Allow-Credentials": true, // Required for cookies, authorization headers with HTTPS
12 | };
13 |
14 | let params = {
15 | TableName: "ISS-task-dns-track-dev",
16 | FilterExpression: "replaced = :n",
17 | ExpressionAttributeValues: {
18 | ":n": "no",
19 | },
20 | ExpressionAttributeNames: {
21 | "#serverPosition": "role",
22 | },
23 | ProjectionExpression: "dns, #serverPosition, replaced, eventid",
24 | };
25 |
26 | const scanResult = await scanDynamo(params);
27 | if (scanResult.Count < 1) {
28 | console.log("Nothing yet in the table");
29 | let response =
30 | "You need to deploy the backend container before loading this page";
31 | let resp = {
32 | statusCode: 503,
33 | headers: headers,
34 | body: JSON.stringify(response),
35 | };
36 | return resp;
37 | }
38 |
39 | const response = {
40 | statusCode: 200,
41 | headers: headers,
42 | body: JSON.stringify(scanResult),
43 | };
44 | return response;
45 |
46 | async function scanDynamo(params) {
47 | console.log("Scanning DynamoBD");
48 | let result;
49 | await dynamodb
50 | .scan(params, function (err, data) {
51 | if (err) console.log(err, err.stack);
52 | else result = data;
53 | })
54 | .promise();
55 | return result;
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/amplify.state:
--------------------------------------------------------------------------------
1 | {
2 | "pluginId": "amplify-nodejs-function-runtime-provider",
3 | "functionRuntime": "nodejs",
4 | "useLegacyBuild": true,
5 | "defaultEditorFile": "src/app.js"
6 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/custom-policies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Action": [],
4 | "Resource": []
5 | }
6 | ]
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/function-parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "lambdaLayers": []
3 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/saveIVSparam-cloudformation-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"Amplify\",\"createdWith\":\"8.3.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}",
4 | "Parameters": {
5 | "CloudWatchRule": {
6 | "Type": "String",
7 | "Default": "NONE",
8 | "Description": " Schedule Expression"
9 | },
10 | "deploymentBucketName": {
11 | "Type": "String"
12 | },
13 | "env": {
14 | "Type": "String"
15 | },
16 | "s3Key": {
17 | "Type": "String"
18 | },
19 | "storageIVSparamName": {
20 | "Type": "String",
21 | "Default": "storageIVSparamName"
22 | },
23 | "storageIVSparamArn": {
24 | "Type": "String",
25 | "Default": "storageIVSparamArn"
26 | }
27 | },
28 | "Conditions": {
29 | "ShouldNotCreateEnvResources": {
30 | "Fn::Equals": [
31 | {
32 | "Ref": "env"
33 | },
34 | "NONE"
35 | ]
36 | }
37 | },
38 | "Resources": {
39 | "LambdaFunction": {
40 | "Type": "AWS::Lambda::Function",
41 | "Metadata": {
42 | "aws:asset:path": "./src",
43 | "aws:asset:property": "Code"
44 | },
45 | "Properties": {
46 | "Code": {
47 | "S3Bucket": {
48 | "Ref": "deploymentBucketName"
49 | },
50 | "S3Key": {
51 | "Ref": "s3Key"
52 | }
53 | },
54 | "Handler": "index.handler",
55 | "FunctionName": {
56 | "Fn::If": [
57 | "ShouldNotCreateEnvResources",
58 | "saveIVSparam",
59 | {
60 | "Fn::Join": [
61 | "",
62 | [
63 | "saveIVSparam",
64 | "-",
65 | {
66 | "Ref": "env"
67 | }
68 | ]
69 | ]
70 | }
71 | ]
72 | },
73 | "Environment": {
74 | "Variables": {
75 | "ENV": {
76 | "Ref": "env"
77 | },
78 | "REGION": {
79 | "Ref": "AWS::Region"
80 | }
81 | }
82 | },
83 | "Role": {
84 | "Fn::GetAtt": [
85 | "LambdaExecutionRole",
86 | "Arn"
87 | ]
88 | },
89 | "Runtime": "nodejs14.x",
90 | "Layers": [],
91 | "Timeout": 25
92 | }
93 | },
94 | "LambdaExecutionRole": {
95 | "Type": "AWS::IAM::Role",
96 | "Properties": {
97 | "RoleName": {
98 | "Fn::If": [
99 | "ShouldNotCreateEnvResources",
100 | "simplewebstreamingLambdaRole98f52dca",
101 | {
102 | "Fn::Join": [
103 | "",
104 | [
105 | "simplewebstreamingLambdaRole98f52dca",
106 | "-",
107 | {
108 | "Ref": "env"
109 | }
110 | ]
111 | ]
112 | }
113 | ]
114 | },
115 | "AssumeRolePolicyDocument": {
116 | "Version": "2012-10-17",
117 | "Statement": [
118 | {
119 | "Effect": "Allow",
120 | "Principal": {
121 | "Service": [
122 | "lambda.amazonaws.com"
123 | ]
124 | },
125 | "Action": [
126 | "sts:AssumeRole"
127 | ]
128 | }
129 | ]
130 | }
131 | }
132 | },
133 | "lambdaexecutionpolicy": {
134 | "DependsOn": [
135 | "LambdaExecutionRole"
136 | ],
137 | "Type": "AWS::IAM::Policy",
138 | "Properties": {
139 | "PolicyName": "lambda-execution-policy",
140 | "Roles": [
141 | {
142 | "Ref": "LambdaExecutionRole"
143 | }
144 | ],
145 | "PolicyDocument": {
146 | "Version": "2012-10-17",
147 | "Statement": [
148 | {
149 | "Effect": "Allow",
150 | "Action": [
151 | "logs:CreateLogGroup",
152 | "logs:CreateLogStream",
153 | "logs:PutLogEvents"
154 | ],
155 | "Resource": {
156 | "Fn::Sub": [
157 | "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
158 | {
159 | "region": {
160 | "Ref": "AWS::Region"
161 | },
162 | "account": {
163 | "Ref": "AWS::AccountId"
164 | },
165 | "lambda": {
166 | "Ref": "LambdaFunction"
167 | }
168 | }
169 | ]
170 | }
171 | },
172 | {
173 | "Effect": "Allow",
174 | "Action": [
175 | "dynamodb:DescribeTable",
176 | "dynamodb:GetItem",
177 | "dynamodb:Query",
178 | "dynamodb:Scan",
179 | "dynamodb:PutItem",
180 | "dynamodb:UpdateItem",
181 | "dynamodb:DeleteItem"
182 | ],
183 | "Resource": [
184 | {
185 | "Ref": "storageIVSparamArn"
186 | },
187 | {
188 | "Fn::Join": [
189 | "/",
190 | [
191 | {
192 | "Ref": "storageIVSparamArn"
193 | },
194 | "index/*"
195 | ]
196 | ]
197 | }
198 | ]
199 | }
200 | ]
201 | }
202 | }
203 | }
204 | },
205 | "Outputs": {
206 | "Name": {
207 | "Value": {
208 | "Ref": "LambdaFunction"
209 | }
210 | },
211 | "Arn": {
212 | "Value": {
213 | "Fn::GetAtt": [
214 | "LambdaFunction",
215 | "Arn"
216 | ]
217 | }
218 | },
219 | "Region": {
220 | "Value": {
221 | "Ref": "AWS::Region"
222 | }
223 | },
224 | "LambdaExecutionRole": {
225 | "Value": {
226 | "Ref": "LambdaExecutionRole"
227 | }
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/src/event.json:
--------------------------------------------------------------------------------
1 | {
2 | "httpMethod": "POST",
3 | "path": "/item",
4 | "queryStringParameters": {
5 | "limit": "10"
6 | },
7 | "headers": {
8 | "Content-Type": "application/json"
9 | },
10 | "body": "{\"msg\":\"Hello from the event.json body\"}"
11 | }
12 |
--------------------------------------------------------------------------------
/frontend/amplify/backend/function/saveIVSparam/src/index.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 | const AWS = require("aws-sdk");
4 | const dynamodb = new AWS.DynamoDB();
5 |
6 | exports.handler = async (event) => {
7 | var response;
8 | // GET Channel Configuration
9 | if (event.httpMethod == "GET") {
10 | const getChannel = async () => {
11 | var user = event.path.substring(event.path.lastIndexOf("/") + 1);
12 | var channelParams = {
13 | TableName: "IVSparam-dev",
14 | Key: {
15 | "username": {
16 | S: user,
17 | },
18 | },
19 | };
20 | let result = await dynamodb
21 | .getItem(channelParams, function (err, data) {
22 | if (err) console.log(err, err.stack);
23 | })
24 | .promise();
25 | return result.Item;
26 | };
27 | await getChannel()
28 | .then((result) => {
29 | //console.log("line 28 ~ awaitgetChannel ~ result", result);
30 | response = result;
31 | })
32 | .catch();
33 | } else {
34 | // Create Channel Configuration
35 | var body = JSON.parse(event.body);
36 | const createChannel = async () => {
37 | var channelParams = {
38 | TableName: "IVSparam-dev",
39 | Item: {
40 | "username": { S: body.username },
41 | "rtmpURL": { S: body.rtmpURL },
42 | "channelType": { S: body.channelType },
43 | "streamKey": { S: body.streamKey },
44 | "playURL": { S: body.playURL },
45 | },
46 | };
47 |
48 | let result = await dynamodb
49 | .putItem(channelParams, function (err, data) {
50 | if (err) console.log(err, err.stack);
51 | })
52 | .promise();
53 | return result;
54 | };
55 |
56 | await createChannel()
57 | .then((result) => {
58 | //console.log("line 62 ~ createChannel ~ resp", result);
59 | response = result;
60 | })
61 | .catch();
62 | }
63 |
64 | return {
65 | statusCode: 200,
66 | headers: {
67 | "Access-Control-Allow-Origin": "*",
68 | "Access-Control-Allow-Headers": "*",
69 | },
70 | body: JSON.stringify(response),
71 | };
72 | };
73 |
--------------------------------------------------------------------------------
/frontend/amplify/backend/storage/ISStaskdnstrack/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceName": "ISStaskdnstrack",
3 | "tableName": "ISS-task-dns-track",
4 | "partitionKey": {
5 | "fieldName": "role",
6 | "fieldType": "string"
7 | },
8 | "gsi": [],
9 | "triggerFunctions": []
10 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/storage/IVSparam/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceName": "IVSparam",
3 | "tableName": "IVSparam",
4 | "partitionKey": {
5 | "fieldName": "username",
6 | "fieldType": "string"
7 | },
8 | "gsi": [],
9 | "triggerFunctions": []
10 | }
--------------------------------------------------------------------------------
/frontend/amplify/backend/tags.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Key": "user:Stack",
4 | "Value": "{project-env}"
5 | },
6 | {
7 | "Key": "user:Application",
8 | "Value": "{project-name}"
9 | }
10 | ]
--------------------------------------------------------------------------------
/frontend/amplify/backend/types/amplify-dependent-resources-ref.d.ts:
--------------------------------------------------------------------------------
1 | export type AmplifyDependentResourcesAttributes = {
2 | "function": {
3 | "saveIVSparam": {
4 | "Name": "string",
5 | "Arn": "string",
6 | "Region": "string",
7 | "LambdaExecutionRole": "string"
8 | },
9 | "ISSgetServers": {
10 | "Name": "string",
11 | "Arn": "string",
12 | "Region": "string",
13 | "LambdaExecutionRole": "string"
14 | }
15 | },
16 | "auth": {
17 | "simplewebstreaming178ae288": {
18 | "IdentityPoolId": "string",
19 | "IdentityPoolName": "string",
20 | "UserPoolId": "string",
21 | "UserPoolArn": "string",
22 | "UserPoolName": "string",
23 | "AppClientIDWeb": "string",
24 | "AppClientID": "string"
25 | }
26 | },
27 | "storage": {
28 | "IVSparam": {
29 | "Name": "string",
30 | "Arn": "string",
31 | "StreamArn": "string",
32 | "PartitionKeyName": "string",
33 | "PartitionKeyType": "string",
34 | "Region": "string"
35 | },
36 | "ISStaskdnstrack": {
37 | "Name": "string",
38 | "Arn": "string",
39 | "StreamArn": "string",
40 | "PartitionKeyName": "string",
41 | "PartitionKeyType": "string",
42 | "Region": "string"
43 | }
44 | },
45 | "api": {
46 | "saveIVSparam": {
47 | "RootUrl": "string",
48 | "ApiName": "string",
49 | "ApiId": "string"
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/frontend/amplify/hooks/README.md:
--------------------------------------------------------------------------------
1 | # Command Hooks
2 |
3 | Command hooks can be used to run custom scripts upon Amplify CLI lifecycle events like pre-push, post-add-function, etc.
4 |
5 | To get started, add your script files based on the expected naming convention in this directory.
6 |
7 | Learn more about the script file naming convention, hook parameters, third party dependencies, and advanced configurations at https://docs.amplify.aws/cli/usage/command-hooks
8 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ivs-simple-streaming",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "@aws-amplify/api": "^4.0.39",
7 | "@aws-amplify/auth": "^4.5.3",
8 | "@aws-amplify/cache": "^4.0.41",
9 | "@aws-amplify/core": "^4.5.3",
10 | "@aws-amplify/ui-react": "^2.17.0",
11 | "bootstrap-icons": "^1.8.1",
12 | "react": "^18.1.0",
13 | "react-dom": "^18.1.0",
14 | "react-router-dom": "^6.3.0",
15 | "react-scripts": "5.0.1"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "browserslist": {
24 | "production": [
25 | ">0.2%",
26 | "not dead",
27 | "not op_mini all"
28 | ],
29 | "development": [
30 | "last 1 chrome version",
31 | "last 1 firefox version",
32 | "last 1 safari version"
33 | ]
34 | },
35 | "devDependencies": {
36 | "webpack-bundle-analyzer": "^4.5.0",
37 | "webpack-cli": "^4.9.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
17 |
18 |
27 | AWS - Simple Web Broadcasting IVS
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/frontend/public/logo192.png
--------------------------------------------------------------------------------
/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/aws-simple-streaming-webapp/c2d271f79527f20b75628247947bac7584aa3d40/frontend/public/logo512.png
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/frontend/src/components/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | text-align: center;
3 | color: white;
4 | }
5 |
6 | body nav a {
7 | font-size: large;
8 | padding-left: 10px;
9 | padding-right: 10px;
10 | }
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/frontend/src/components/App.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React, { useEffect, useState } from "react";
5 | import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
6 | import UserMedia from "./UserMedia";
7 | import HomePage from "./HomePage";
8 | import PlayerView from "./PlayerView";
9 | import "./App.css";
10 |
11 | import Amplify from "@aws-amplify/core";
12 | import Auth from "@aws-amplify/auth";
13 | import { withAuthenticator } from "@aws-amplify/ui-react";
14 | import awsmobile from "../aws-exports";
15 | import "@aws-amplify/ui-react/styles.css";
16 |
17 | Amplify.configure(awsmobile);
18 | Auth.configure(awsmobile);
19 |
20 | function App(props) {
21 | const [username, setUsername] = useState();
22 |
23 | useEffect(() => {
24 | (async function () {
25 | try {
26 | await Auth.currentAuthenticatedUser({ bypassCache: false }).then(
27 | (user) => {
28 | console.log(user);
29 | setUsername(user.username);
30 | }
31 | );
32 | } catch (e) {
33 | console.error("Error, no logeeed user ", e);
34 | }
35 | })();
36 | console.log("component mounted!");
37 | }, []);
38 |
39 | const signOut = () => {
40 | Auth.signOut();
41 | };
42 |
43 | const openModal = () => {
44 | console.log("Ok!Here");
45 | };
46 |
47 | return username ? (
48 |
49 |
50 |
51 | Simple Streaming
52 |
53 | Open Player
54 |
55 |
56 | Logout
57 |
58 |
59 |
60 | } />
61 | }
65 | />
66 | }
70 | />
71 | }
75 | />
76 |
77 |
78 |
79 | ) : (
80 | Loading
81 | );
82 | }
83 |
84 | export default withAuthenticator(App);
85 |
--------------------------------------------------------------------------------
/frontend/src/components/HomePage.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React, { useState, useEffect, useRef } from "react";
5 | import UserMedia from "./UserMedia";
6 | import StreamForm from "./StreamForm"; // add lazy load
7 | import { API } from "@aws-amplify/api";
8 |
9 | import "./styles/HomePage.style.css";
10 |
11 | export default function HomePage(props) {
12 | const username = props.username;
13 | const mediaRecorder = useRef();
14 | const canvasRef = useRef();
15 | const audioRef = useRef();
16 | const [streamParams, setStreamParams] = useState({ url: "", key: "" });
17 | const [streaming, setStreaming] = useState(false);
18 | const [configured, setConfigured] = useState(false);
19 | const [wrapServers, setWrapServers] = useState("");
20 | const wsRef = useRef();
21 | const btnPlayer = document.querySelector("#openplayer");
22 |
23 | useEffect(() => {
24 | (async function () {
25 | if (!wrapServers) {
26 | try {
27 | let apiName = "saveIVSparam";
28 | let path = `/getServers/`;
29 | await API.get(apiName, path).then((response) => {
30 | if (response.Items.length === 0) {
31 | console.err("Server is not defined");
32 | } else {
33 | console.log("Remote server is:", response.Items[0].dns);
34 | setWrapServers(response.Items[0].dns);
35 | addDebugLine(
36 | Date.now(),
37 | `Remote server has been defined, using ${response.Items[0].dns}`
38 | );
39 | }
40 | });
41 | } catch (error) {
42 | console.log(error);
43 | handleError(error);
44 | }
45 | } else console.log("Remote server is, cached:", wrapServers);
46 | })();
47 | }, [streaming, configured]);
48 |
49 | function handleError(e) {
50 | console.log("The server request returned null", e);
51 | console.log("Assuming localhost");
52 | console.log("localhost:3004");
53 | setWrapServers("127.0.0.1:3004");
54 | addDebugLine(
55 | Date.now(),
56 | `No Remote server has been registered, assuming localhost:3004`
57 | );
58 | }
59 |
60 | function handleFormReady(url, key, playurl) {
61 | setStreamParams({ url: url, key: key });
62 | btnPlayer.addEventListener("click", () => openPlayer(playurl));
63 | const btnSave = document.querySelector("#btnsave");
64 | btnSave.addEventListener("click", (e) => {
65 | e.preventDefault();
66 | btnSave.classList.add("saved");
67 | setConfigured(true);
68 | addDebugLine(Date.now(), `Stream Params Saved, ready to stream`);
69 | });
70 | }
71 |
72 | function openPlayer(url) {
73 | console.log(url);
74 | window.open(`/PlayerView?url=${url}`, "_blank");
75 | }
76 |
77 | async function handleCameraReady(video, canvasVideo, audio) {
78 | if (!testBrowserSupport()) {
79 | console.log(
80 | "Browser does not support HTMLMediaElement.prototype.captureStream;"
81 | );
82 | canvasRef.current = canvasVideo;
83 | } else {
84 | console.log("Browser support Ok!, using video element");
85 | canvasRef.current = video;
86 | }
87 | audioRef.current = audio;
88 | }
89 |
90 | function testBrowserSupport() {
91 | return "function" === typeof HTMLMediaElement.prototype.captureStream;
92 | }
93 |
94 | async function startStream(e) {
95 | e.preventDefault();
96 | if (!wsRef.current)
97 | await socketConnect(wrapServers, streamParams.url, streamParams.key);
98 |
99 | const videoStreaming = canvasRef.current.captureStream(30);
100 | const audioStreaming = new MediaStream();
101 |
102 | audioRef.current.forEach(function (track) {
103 | console.log("track", track);
104 | audioStreaming.addTrack(track);
105 | });
106 |
107 | console.log("audioStreaming", audioStreaming);
108 | const outputStream = new MediaStream();
109 | [audioStreaming, videoStreaming].forEach(function (s) {
110 | s.getTracks().forEach(function (t) {
111 | outputStream.addTrack(t);
112 | });
113 | });
114 |
115 | let options = getmimeType();
116 | console.log("mimeType is", options);
117 |
118 | mediaRecorder.current = new MediaRecorder(outputStream, options);
119 |
120 | mediaRecorder.current.addEventListener("dataavailable", (e) => {
121 | console.log("Stream data!!!", e);
122 | wsRef.current.send(e.data);
123 | });
124 | mediaRecorder.current.start(1000);
125 | }
126 |
127 | function getmimeType() {
128 | let options = {
129 | mimeType: "video/webm;codecs=h264",
130 | videoBitsPerSecond: 3000000,
131 | };
132 | options.codec = "h264";
133 |
134 | if (!MediaRecorder.isTypeSupported(options.mimeType)) {
135 | addDebugLine(Date.now(), `${options.mimeType} + is not Supported`);
136 | options = {
137 | mimeType: "video/webm;codes=vp8,opus",
138 | videoBitsPerSecond: 3000000,
139 | };
140 | options.codec = "vp8o";
141 |
142 | if (!MediaRecorder.isTypeSupported(options.mimeType)) {
143 | addDebugLine(Date.now(), `${options.mimeType} + is not Supported`);
144 | options = {
145 | mimeType: "video/mp4;codecs=avc1,mp4a",
146 | videoBitsPerSecond: 3000000,
147 | };
148 | options.codec = "mp4a";
149 | }
150 | }
151 | return options;
152 | }
153 |
154 | function stopStreaming(e) {
155 | mediaRecorder.current.stop();
156 | setStreaming(false);
157 | }
158 |
159 | async function socketConnect(server, rtmpURL, streamKey) {
160 | console.log("socketConnect");
161 | let options = getmimeType();
162 | let codec = options.codec;
163 | console.log("??", codec);
164 | if (window.location.protocol == "http:") {
165 | let protocol = window.location.protocol.replace("http", "ws");
166 | let testServer = "127.0.0.1:3004";
167 | var wsUrl = `${protocol}//${server}/rtmps/${codec}/${rtmpURL}${streamKey}`; // if you want to force streaming to a local server change to use testServer, instead of server
168 | } else {
169 | let protocol = window.location.protocol.replace("https", "wss");
170 | var wsUrl = `${protocol}//${server}/rtmps/${codec}/${rtmpURL}${streamKey}`;
171 | }
172 | console.log("URL", wsUrl);
173 | wsRef.current = new WebSocket(wsUrl);
174 |
175 | wsRef.current.onerror = (err) => {
176 | console.error("Server not available", err);
177 | stopStreaming();
178 | addDebugLine(Date.now(), `Error connecting to socket`);
179 | return;
180 | };
181 | // onclose sub-function, please note::, Fargate container takes a few minutes to start at the deployment
182 | wsRef.current.onclose = (e) => {
183 | console.log("Socket Closed", server, e.code, e);
184 | if (e.code == 1006) {
185 | console.log("timeout");
186 | wsRef.current = null;
187 | }
188 | if (e.code == 1015) {
189 | console.log("tls error");
190 | wsRef.current = null;
191 | }
192 | addDebugLine(Date.now(), `Error on connecting to socket ${e.code}`);
193 | setStreaming(false);
194 | stopStreaming();
195 | return;
196 | };
197 |
198 | //wsRef.current.addEventListener("open", async function open(data) {
199 | // console.log("Open!!!!!", data);
200 | //});
201 |
202 | wsRef.current.onmessage = (evt) => {
203 | addDebugLine(Date.now(), evt.data);
204 | };
205 | }
206 |
207 | function addDebugLine(metadataTime, metadataText) {
208 | const domString = `
209 | ${metadataTime}
210 | ${metadataText} `.trim();
211 |
212 | const dataLine = document.createElement("div");
213 | dataLine.classList.add("class", "data-line");
214 | dataLine.innerHTML = domString;
215 |
216 | const debugData = document.querySelector(".debug-data");
217 | debugData.appendChild(dataLine);
218 | }
219 |
220 | /*################################### WebSocket Client ###################################*/
221 | return wrapServers ? (
222 |
223 |
249 |
250 |
251 |
252 |
255 |
256 | ) : (
257 | Loading...
258 | );
259 | }
260 |
--------------------------------------------------------------------------------
/frontend/src/components/PlayerView.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React, { useRef } from "react";
5 | import VideoPlayer from "./player/player";
6 | import "./styles/PlayerView.style.css";
7 |
8 | export default function PlayerView() {
9 | const urlSearchParams = new URLSearchParams(window.location.search);
10 | const params = Object.fromEntries(urlSearchParams.entries());
11 | const playerRef = useRef(null);
12 |
13 | const videoURL = params.url;
14 | console.log(videoURL);
15 |
16 | const videoJsOptions = {
17 | autoplay: "muted", //mute audio when page loads, but auto play video
18 | controls: true,
19 | responsive: true,
20 | fluid: true,
21 | width: 896,
22 | height: 504,
23 | sources: [
24 | {
25 | src: videoURL,
26 | type: "application/x-mpegURL",
27 | },
28 | ],
29 | };
30 |
31 | const handlePlayerReady = (player) => {
32 | player.on("waiting", () => {
33 | console.log("player is waiting");
34 | });
35 |
36 | player.on("dispose", () => {
37 | console.log("player will dispose");
38 | });
39 |
40 | player.on("playing", () => {
41 | console.log("player playing");
42 | addDebugLine(Date.now(), "Player playing");
43 | });
44 |
45 | player.on("error", (err) => {
46 | console.log("Play Error", err);
47 | addDebugLine(Date.now(), `Player ${err.type} ${err.target.innerText}`);
48 | });
49 |
50 | playerRef.current = player;
51 | };
52 |
53 | function addDebugLine(metadataTime, metadataText) {
54 | const domString = `
55 | ${metadataTime}
56 | ${metadataText} `.trim();
57 |
58 | const dataLine = document.createElement("div");
59 | dataLine.classList.add("class", "data-line");
60 | dataLine.innerHTML = domString;
61 |
62 | const debugData = document.querySelector(".debug-data");
63 | debugData.appendChild(dataLine);
64 | }
65 |
66 | return (
67 |
68 |
69 |
Video Player
70 |
71 |
76 |
77 |
80 |
81 |
82 | );
83 | }
84 |
--------------------------------------------------------------------------------
/frontend/src/components/StreamForm.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React, { useEffect, useState } from "react";
5 | import "./styles/StreamForm.style.css";
6 | import { API } from "@aws-amplify/api";
7 |
8 | export default function StreamForm(props) {
9 | const username = props.username;
10 | const [rtmpURL, setRtmpURL] = useState("");
11 | const [channelType, setChannelType] = useState("");
12 | const [streamKey, setStreamKey] = useState("");
13 | const [playURL, setPlayURL] = useState("");
14 | const [configured, isConfigure] = useState(null);
15 | const { onReady } = props;
16 |
17 | useEffect(() => {
18 | (async function () {
19 | await getStream();
20 | })();
21 | }, [configured]);
22 |
23 | async function getStream() {
24 | let apiName = "saveIVSparam";
25 | let path = `/putitens/${username}`;
26 | await API.get(apiName, path)
27 | .then((ivsparams) => {
28 | if (ivsparams) {
29 | setChannelType(ivsparams.channelType.S);
30 | setRtmpURL(ivsparams.rtmpURL.S);
31 | setStreamKey(ivsparams.streamKey.S);
32 | setPlayURL(ivsparams.playURL.S);
33 | isConfigure(true);
34 | onReady &&
35 | onReady(
36 | ivsparams.rtmpURL.S,
37 | ivsparams.streamKey.S,
38 | ivsparams.playURL.S,
39 | configured
40 | );
41 | } else isConfigure(false);
42 | })
43 | .catch((error) => {
44 | console.log(error);
45 | });
46 | }
47 |
48 | const storeStream = (e) => {
49 | e.preventDefault();
50 | let apiName = "saveIVSparam";
51 | let path = "/putitens";
52 | let data = {
53 | body: {
54 | username,
55 | channelType,
56 | rtmpURL,
57 | streamKey,
58 | playURL,
59 | },
60 | };
61 | API.post(apiName, path, data)
62 | .then((response) => {
63 | getStream();
64 | })
65 | .catch((error) => {
66 | console.log(error.response);
67 | });
68 | };
69 |
70 | return (
71 |
152 | );
153 | }
154 |
--------------------------------------------------------------------------------
/frontend/src/components/UserMedia.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React, { useEffect, useRef, useState } from "react";
5 | import "./styles/UserMedia.style.css";
6 |
7 | export default function HomePage(props) {
8 | const { onReady } = props;
9 | const mediaStream = useRef();
10 | const mediaDevices = useRef();
11 | const localStream = useRef();
12 | const canvasStream = useRef();
13 | const animationRef = useRef();
14 | const [vDevID, setVDevID] = useState("");
15 | const [aDevID, setADevID] = useState("");
16 | const [devices, setDevices] = useState({
17 | videoin: null,
18 | audioin: null,
19 | audioout: null,
20 | });
21 | const constraints = {
22 | audio: { autoplay: true, deviceId: aDevID, sampleRate: 44100 },
23 | video: { width: 1280, height: 720, deviceId: vDevID },
24 | };
25 | const [errorMSG, setErrorMSG] = useState(null);
26 |
27 | useEffect(() => {
28 | (async function () {
29 | try {
30 | mediaStream.current = await navigator.mediaDevices.getUserMedia(
31 | constraints
32 | );
33 | mediaDevices.current = await navigator.mediaDevices.enumerateDevices();
34 | deviceList();
35 | //getCamera();
36 | } catch (e) {
37 | console.error("Device Error", e);
38 | handleError(e);
39 | }
40 | })();
41 | }, [vDevID, aDevID]);
42 |
43 | useEffect(() => {
44 | window.addEventListener("resize", onResize);
45 | }, [localStream.current]);
46 |
47 | function onResize(e) {
48 | if (
49 | canvasStream.current.width !== localStream.current.clientHeight ||
50 | canvasStream.current.width !== localStream.current.clientWidth
51 | ) {
52 | console.log("Fixing video sizing");
53 | canvasStream.current.width = localStream.current.clientHeight;
54 | canvasStream.current.width = localStream.current.clientWidth;
55 | }
56 |
57 | requestAnimationFrame(refreshCanvas);
58 | }
59 |
60 | /*################################### Function List Devices ###################################*/
61 | function deviceList() {
62 | console.log("List Cameras", mediaDevices.current.length);
63 | let vidin = [];
64 | let auin = [];
65 | let audioOut = [];
66 | mediaDevices.current.forEach(function (gotDevice) {
67 | let i = 0;
68 | if (gotDevice.kind === "audioinput") {
69 | auin.push({ label: gotDevice.label, id: gotDevice.deviceId, len: i++ });
70 | } else if (gotDevice.kind === "videoinput") {
71 | vidin.push({ label: gotDevice.label, id: gotDevice.deviceId });
72 | } else if (gotDevice.kind === "audiooutput") {
73 | audioOut.push({ label: gotDevice.label, id: gotDevice.deviceId });
74 | } else {
75 | console.log("Some other kind of source/device: ", gotDevice);
76 | }
77 | });
78 | setDevices({ audioin: auin, videoin: vidin, audioout: audioOut });
79 | getCamera();
80 | }
81 |
82 | /*################################### Handle Device Change ###################################*/
83 | const handleDevChange = (event) => {
84 | event.preventDefault();
85 | console.log("Device Change block", vDevID, aDevID, constraints);
86 | console.log(event.target.value, event.target.id);
87 | if (event.target.id === "videoin") {
88 | console.log("set video", event.target.value);
89 | setVDevID(event.target.value);
90 | }
91 | if (event.target.id === "audioin") {
92 | console.log("set audio iN", aDevID);
93 | setADevID(event.target.value);
94 | }
95 | if (event.target.id === "audioout") {
96 | console.log("set audio out");
97 | }
98 | getCamera();
99 | };
100 |
101 | /*################################### Function Display Camera ###################################*/
102 | const getCamera = async () => {
103 | console.log("GET Local Camera");
104 | window.localStream = mediaStream.current;
105 | localStream.current.srcObject = mediaStream.current;
106 | localStream.current.onloadedmetadata = async function (e) {
107 | await localStream.current.play();
108 | };
109 |
110 | canvasStream.current.height = localStream.current.clientHeight;
111 | canvasStream.current.width = localStream.current.clientWidth;
112 |
113 | let audioTracks = mediaStream.current.getAudioTracks();
114 | requestAnimationFrame(refreshCanvas);
115 | onReady && onReady(localStream.current, canvasStream.current, audioTracks);
116 | };
117 |
118 | //Add to canvas
119 | const refreshCanvas = (e) => {
120 | var element = document.getElementById("video");
121 | if (element) {
122 | const ctx = canvasStream.current.getContext("2d");
123 |
124 | let cW = canvasStream.current.width;
125 | let cH = canvasStream.current.height;
126 |
127 | draw(localStream.current, cW, cH);
128 |
129 | function draw(video, width, height) {
130 | ctx.drawImage(video, 0, 0, width, height); // canvas has a distort
131 | }
132 | requestAnimationFrame(refreshCanvas);
133 | }
134 | };
135 |
136 | /*################################### Function Handle Cam Error ###################################*/
137 | const handleError = (error) => {
138 | if (error.name === "ConstraintNotSatisfiedError") {
139 | let v = constraints.video;
140 | console.error(
141 | `The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`
142 | );
143 | } else if (error.name === "NotAllowedError") {
144 | console.error(
145 | "Permissions have not been granted to use your camera and " +
146 | "microphone, you need to allow the page access to your devices in " +
147 | "order for the demo to work."
148 | );
149 | }
150 | console.error(`getUserMedia error: ${error.name}`, error);
151 | setErrorMSG(error.name);
152 | };
153 |
154 | return devices.videoin ? (
155 |
156 |
157 |
195 |
196 |
197 |
205 |
206 |
207 |
208 | ) : (
209 | loading...
210 | );
211 | }
212 |
--------------------------------------------------------------------------------
/frontend/src/components/player/player.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const VideoJS = (props) => {
4 | const videoRef = React.useRef(null);
5 | const playerRef = React.useRef(null);
6 | const { options, onReady } = props;
7 |
8 | React.useEffect(() => {
9 | // Make sure Video.js player is only initialized once
10 | if (!playerRef.current) {
11 | const videoElement = videoRef.current;
12 |
13 | if (!videoElement) return;
14 |
15 | const player = (playerRef.current = videojs(videoElement, options, () => {
16 | player.log("player is ready");
17 | onReady && onReady(player);
18 | }));
19 |
20 | // You can update player in the `else` block here, for example:
21 | } else {
22 | player.autoplay(options.autoplay);
23 | player.src(options.sources);
24 | }
25 | }, [options, videoRef]);
26 |
27 | // Dispose the Video.js player when the functional component unmounts
28 | React.useEffect(() => {
29 | const player = playerRef.current;
30 |
31 | return () => {
32 | if (player) {
33 | player.dispose();
34 | playerRef.current = null;
35 | }
36 | };
37 | }, [playerRef]);
38 |
39 | return (
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default VideoJS;
47 |
--------------------------------------------------------------------------------
/frontend/src/components/styles/HomePage.style.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: rgb(0, 0, 0);
3 | }
4 |
5 | .App{
6 | position: relative;
7 | margin: auto;
8 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.30);
9 | border-radius: 6px;
10 | margin: auto;
11 | max-width: 1000px;
12 | width: 90%;
13 | display: flex;
14 | justify-content: center;
15 | flex-direction: column;
16 | }
17 |
18 | .stream-container {
19 | position: relative;
20 | margin: auto;
21 | display: flex;
22 | justify-content: center;
23 | flex-direction: column;
24 | border-radius: 6px;
25 | }
26 |
27 | .controls-container {
28 | position: absolute;
29 | bottom: 0;
30 | left: 0;
31 | right: 0;
32 | z-index: 100;
33 | opacity: 0.5;
34 | transition: opacity 150ms ease-in-out;
35 | outline: none;
36 | max-height: 100%;
37 | height: auto;
38 | }
39 |
40 | .rounded-btn {
41 | border-radius: 100%;
42 | height: 80px;
43 | width: 80px;
44 | font-size: 16px;
45 | font-weight: bold;
46 | color: white;
47 | background-color: #9146FF;
48 | opacity: 0.9;
49 | border: none;
50 | outline: none;
51 | }
52 |
53 | .controls-container:hover,
54 | .rounded-start-btn:hover {
55 | opacity: 1;
56 | outline: none;
57 | }
58 |
59 | .rounded-btn.stop {
60 | background-color: #ff4646;
61 | outline: none;
62 | }
63 |
64 | .placeholder-container{
65 | position: relative;
66 | left: 0;
67 | top: 10px;
68 | right: 0;
69 | bottom: 0px;
70 | display: flex;
71 | justify-content: center;
72 | align-items: center;
73 | margin: auto;
74 | max-width: 800px;
75 | margin-bottom: 10px;
76 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.30);
77 | }
78 |
79 | .placeholder-content {
80 | position: relative;
81 | flex-shrink: 0;
82 | margin: center;
83 | word-break: break-all;
84 | transition: all 0.15s ease-in-out;
85 | animation: fadeIn 0.25s 1;
86 | width: 70%;
87 | padding: 40px;
88 | height: 100px;
89 | color: #4a4a4a;
90 | color: #4a4a4a;
91 | font-family: monospace;
92 | font-size:large;
93 | }
94 |
95 | .debug-container {
96 | padding-top: 10px;
97 | position: relative;
98 | max-width: 1000px;
99 | padding-bottom: 20px;
100 | }
101 |
102 | .debug-data {
103 | width: 80%;
104 | height: 150px;
105 | margin: auto;
106 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.30);
107 | justify-content: center;
108 | flex-direction: column;
109 | border: 0rem;
110 | color: #4a4a4a;
111 | font-family: monospace;
112 | background: #fff;
113 | border-radius: 0 0 6px 6px;
114 | display: flex;
115 | flex-shrink: 0;
116 | overflow-x: hidden;
117 | overflow-y: scroll;
118 | flex-direction: column;
119 | }
120 |
121 | .data-line {
122 | display: flex;
123 | flex-shrink: 0;
124 | margin: 0 5px 0 0;
125 | padding: 3px 5px 5px 10px;
126 | word-break: break-all;
127 | transition: all 0.15s ease-in-out;
128 | animation: fadeIn 0.25s 1;
129 | }
130 |
131 | .data-line:last-child {
132 | padding-bottom: 2px;
133 | }
134 |
135 | .debug-data__time {
136 | min-width: 5rem;
137 | font-weight: 300;
138 | font-size: 12px;
139 | padding-right: 5px;
140 | }
141 |
142 | .debug-data__value {
143 | font-weight: bold;
144 | font-size: 12px;
145 | }
146 |
147 |
148 | rounded-btn:focus { outline: none; }
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/frontend/src/components/styles/PlayerView.style.css:
--------------------------------------------------------------------------------
1 | .PlayerView {
2 | text-align: center;
3 | }
4 |
5 | .PlayerView h1 {
6 | color: black;
7 | }
8 |
9 | .videoborder{
10 | position: relative;
11 | align-items: center;
12 | justify-content: center;
13 | display: flex;
14 | background-color: aqua;
15 | align-items: center;
16 | border-radius: 10px;
17 | }
18 |
19 | .video-js{
20 | position: relative;
21 | width: 100%;
22 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.30);
23 | }
24 |
25 | .PlayerView .videoplayer {
26 | position: relative;
27 | margin: auto;
28 | align-items: center;
29 | justify-content: center;
30 | width: 90%;
31 | top: 0;
32 | left: 0;
33 | z-index: 100;
34 | }
35 |
36 | .PlayerView .debug-container {
37 | padding-top: 10px;
38 | position: relative;
39 | max-width: 1000px;
40 | padding-bottom: 20px;
41 | margin: auto;
42 | }
43 |
44 | .PlayerView .debug-data {
45 | width: 80%;
46 | height: 150px;
47 | margin: auto;
48 | box-shadow: 0 20px 60px rgba(0, 0, 0, 0.30);
49 | justify-content: center;
50 | flex-direction: column;
51 | border: 0rem;
52 | color: #4a4a4a;
53 | font-family: monospace;
54 | background: #fff;
55 | border-radius: 0 0 6px 6px;
56 | display: flex;
57 | flex-shrink: 0;
58 | overflow-x: hidden;
59 | overflow-y: scroll;
60 | flex-direction: column;
61 | }
62 |
63 | .PlayerView .data-line {
64 | display: flex;
65 | flex-shrink: 0;
66 | margin: 0 5px 0 0;
67 | padding: 3px 5px 5px 10px;
68 | word-break: break-all;
69 | transition: all 0.15s ease-in-out;
70 | animation: fadeIn 0.25s 1;
71 | }
72 |
73 | .PlayerView .data-line:last-child {
74 | padding-bottom: 2px;
75 | }
76 |
77 | .PlayerView .debug-data__time {
78 | min-width: 5rem;
79 | font-weight: 300;
80 | font-size: 12px;
81 | padding-right: 5px;
82 | }
83 |
84 | .PlayerView .debug-data__value {
85 | font-weight: bold;
86 | font-size: 12px;
87 | }
--------------------------------------------------------------------------------
/frontend/src/components/styles/StreamForm.style.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: #484F56;
3 | }
4 |
5 | .textFormivs {
6 | position: relative;
7 | display: flex;
8 | margin: auto;
9 | max-width: 1000px;
10 | flex-direction: column;
11 | }
12 | .textFormivs .form-ivs {
13 | padding-top: 20px;
14 | margin: auto;
15 | }
16 |
17 | .textFormivs .form-URL {
18 | position: relative;
19 | width: auto;
20 | display: flex;
21 | color: #484F56;
22 | box-sizing: border-box;
23 | }
24 | .textFormivs .formLabel {
25 | position: relative;
26 | text-align: center;
27 | color: #484F56;
28 | font-size: 0.8rem;;
29 | }
30 | .textFormivs .formURL {
31 | position: relative;
32 | display: flex;
33 | color: #484F56;
34 | width: auto;
35 | height: 38px;
36 | border-color: #F0F0F0;
37 | border-radius: 6px;
38 | padding-left: 1px ;
39 | font-size: 0.8rem;
40 | }
41 |
42 | .textFormivs .formSelect {
43 | display: flex;
44 | color: #484F56;
45 | width: auto;
46 | height: 38px;
47 | border-color: #F0F0F0;
48 | border-radius: 6px;
49 | font-size: 0.8rem;
50 | }
51 |
52 | .textFormivs .formBot {
53 | position: relative;
54 | display: flex;
55 | background-color: #9146FF;
56 | top: 16px;
57 | border: none;
58 | color: white;
59 | display: inline-block;
60 | font-size: 12px;
61 | height: 40px;
62 | border-radius: 8px;
63 | margin-left: 20px;
64 | }
65 |
66 | .textFormivs .formBot.saved{
67 | background-color: forestgreen;
68 | }
--------------------------------------------------------------------------------
/frontend/src/components/styles/UserMedia.style.css:
--------------------------------------------------------------------------------
1 | body {
2 | text-align: center;
3 | }
4 |
5 | .video-container {
6 | position: relative;
7 | margin: auto;
8 | max-width: 1000px;
9 | width: 90%;
10 | display: flex;
11 | justify-content: center;
12 | flex-direction: column;
13 | padding-bottom: 30px;
14 | margin-bottom: 30px;
15 | }
16 |
17 | .video {
18 | width: 100%;
19 | z-index: -9;
20 | visibility: hidden;
21 | }
22 |
23 |
24 | canvas {
25 | display: flex;
26 | width: 100%;
27 | height: 100%;
28 | position: absolute;
29 | margin: auto;
30 | top: 0;
31 | left: 0;
32 | z-index: 10;
33 | box-shadow: 0 16px 60px rgba(0, 0, 0, 0.30);
34 | }
35 |
36 | .form-control-select {
37 | position: relative;
38 | display: flex;
39 | max-width: 1000px;
40 | text-align: center;
41 | margin: auto;
42 | justify-content: center;
43 | }
44 | .form-control-select .form-control {
45 | box-sizing: border-box;
46 | width: 30%;
47 | margin: 10px;
48 | }
49 | .form-control-select .form-control-play {
50 | width: 540px;
51 | height: 40px;
52 | box-sizing: border-box;
53 | border: 1px solid lightgrey;
54 | border-radius: 5px;
55 | margin: 11px;
56 | }
57 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import React from 'react';
5 | import ReactDOM from 'react-dom';
6 | import App from './components/App';
7 |
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------