Build Interactive Live Video Apps with Amazon IVS
Tags: aws, amazonivs
Published: 2022-04-25 02:52:20

Live streaming is becoming more popular than before as we explore new ways to connect during the restriction in a pandemic situation. In this article, I'll show you how to build your streaming channel using Amazon IVS, so you can host them on your website or applications. Adding a bit of fun, I'll also show you how to add an interactive element to your live streaming to engage your audiences even more.

{% embed https://www.youtube.com/watch?v=gXQc3X1fGx0 %}


> #### Github Repository for Demo(s)
> Code for this article is available at [github.com/donnieprakoso/demo-ivs](https://github.com/donnieprakoso/demo-ivs/tree/main/1-stream-channel-timed-metadata)

---

## Challenges on Running Live Streaming Platform

As we are constantly improving how we deliver information — especially in the current situation — live streaming has become one of the solutions. The idea of disseminating real-time information to (practically) unlimited audiences is one reason for running live streaming.

If we want to live stream into video platforms, i.e. YouTube, Vimeo, we can easily setup our OBS and start streaming. But, there's a case if you want to provide more seamless experience with your business, or to add features that aren't available in the video platform, you will need to host the live stream on your website. 

The biggest challenge in this case is the complexities of managing infrastructure to deliver low-latency videos. Not to mention how hard it is to bring interactivity into the stream. I'm not talking about greeting every viewer who joined the live stream. I'm talking about getting their responses to any questions in real-time.

Wouldn't it be great if we can interact with our viewers? If only we can show some a popup dialog right in the stream and viewers can select the options without having to go to external apps or websites.

Or, in the case of e-commerce live streaming, it would be easier for viewers to shop with the presented items on the stream. They just need to click the item, added into their cart, and proceed with checkout.

Delivering the stream helps us to reach more audiences. Interactivity makes it live.

---

## Here's Amazon IVS

So, imagine that now you can set up your live stream channel with just several clicks. Even better, you can programmatically create the live stream channels for any number of the live stream that you'd like to deliver. These channels can also be embedded into your website or applications. You can also add quizzes, polling, or anything that requires feedback from your viewers.

And the good news is, that those features are what you can expect from Amazon IVS. Amazon Interactive Video Service (IVS), is a managed live streaming solution to create interactive video experiences.

Roughly speaking, this is how it works:

![Working with Amazon IVS](https://dsc.cloud/2c5cc2/ivs-1.png)

First, you need to create a channel in Amazon IVS. You will get a stream key and URL as output from this step.

Second, you need to supply the stream key and URL to your favorite broadcasting channel. We have quite a few options — [OBS](https://obsproject.com/), [Streamlabs](https://streamlabs.com/), [XSplit](https://www.xsplit.com/) — or you can even use an online live streaming studio such as [Streamyard](https://streamyard.com/) or [Restream](https://restream.io/).

The third step is how you want to host the stream. You can use a website using SDK for Web or mobile applications — using SDK for iOS and Android — to host the stream.

The fourth step is optional if you want to add interactivity. You can send metadata payloads into the stream, and your website or mobile apps can receive and process it accordingly.

And that's it. You don't need to provision any infrastructures or expertise to run and scale the live streaming platform.

---

## What We Are Going to Build

If you like what you read so far, the next part will help you to get started. We are going to build a live stream video channel, serve the live stream on a website and send real-time metadata into the stream.

There are 4 main steps in this tutorial.

![Steps in This Tutorial](https://dsc.cloud/2c5cc2/ivs-2.png)

- Step 1: Provision the Amazon IVS channel.

- Step 2: Broadcast a video. In this tutorial, I'm going to use the VLC app to ingest the video into Amazon IVS. You can also use OBS or other broadcasting tools, as long as it supports RTMP protocols.

- Step 3: Run and host a website to serve the live video stream. A quick note in this tutorial, we are going to use a local website. In practice, you can host your website in other AWS services, such as AWS Amplify.

- Step 4: Once everything is set up properly, the last step is to send metadata to demonstrate how we can add interactivity to the live stream.

### Requirements

This tutorial uses the following requirements and please make sure that your development environment satisfies all requirements described below:

| Name    | Version | Where to get                                                                                       |
| ------- | ------- | -------------------------------------------------------------------------------------------------- |
| AWS CDK | 2.17.0  | [github.com/aws/aws-cdk](https://github.com/aws/aws-cdk)                                           |
| Python  | 3.8.13  | [python.org/downloads/release/python-3813/](https://www.python.org/downloads/release/python-3813/) |
| ffmpeg  | 4.4     | [ffmpeg.org/download.html](https://ffmpeg.org/download.html)                                       |
| Sample video | N/A | [peach.blender.org/download/](https://peach.blender.org/download/) |

### Step 0: Clone Github repo

If you'd like to do this tutorial, you can clone this repo: [github.com/donnieprakoso/demo-ivs](https://github.com/donnieprakoso/demo-ivs). Otherwise, carry on reading this tutorial if you'd like to get big pictures of how everything works.

To clone the Git repo, you can run this command on your development environment:

```bash
git clone github.com/donnieprakoso/demo-ivs
```

### Step 1: Provision IVS Channel with AWS CDK

In this step, we are going to provision the IVS channel with AWS CDK. This tutorial uses AWS Cloud Development Kit (CDK) to provide consistent deployment between this tutorial and yours.

![Provision IVS Channel with AWS CDK](https://dsc.cloud/2c5cc2/ivs-3-1.png)

#### **Code Overview**

The `cdk/app.py` code is the main file to provision your IVS channel. Below is the redacted version of the source code, to point out important parts you need to understand.

First, we need to configure the channel. The code below uses the L1 construct of IVS. This channel uses the basic configuration of the IVS channel, without auto-record enabled to Amazon S3 bucket. Noticed that the `authorized` parameter is set to false, which translates to disabling playback authorization.

```python
ivs_channel = ivs.CfnChannel(self, "{}-channel".format(id),  
            authorized=False,  
            latency_mode="LOW",  
            name="demo-ivs-metadata",  
            recording_configuration_arn="",  
            type="STANDARD"  
        )
```

Once we have the channel configured, we need to configure the stream key so we can ingest video into the channel. To pair with the channel, we pass the IVS channel ARN into the `channel_arn` parameter.

```python
ivs_stream_key = ivs.CfnStreamKey(self, "{}-streamkey".format(id),  
            channel_arn=ivs_channel.attr_arn,  
        )
```

Once we have the channel and stream key configured, the next step is to define and instantiates the IVS stack to produce the AWS CloudFormation template. Noticed that we are injecting the account using the environment variable `CDK_DEFAULT_ACCOUNT` and using the `us-east-1` region.

```python
app = App()  
  
env = _cdk.Environment(account=os.environ["CDK_DEFAULT_ACCOUNT"], region="us-east-1")  
IvsChannel(app, "demo1-ivs-metadata", env=env)  
app.synth()
```

#### **Deployment**

To deploy the IVS stack using CDK, follow these steps:

```bash
cd 1-stream-channel-timed-metadata/cdk  
pip install -r requirements.txt  
cdk deploy
```

This will deploy the IVS channel and you'll see the output below:

```bash
Outputs:  
demo1-ivs-metadata.demo1ivsmetadataoutputchannelarn = arn:aws:ivs:us-east-1:XXXXXXXXXX:channel/XXXXXXXXXX  
demo1-ivs-metadata.demo1ivsmetadataoutputchannelingest = XXXXXXXXXX.global-contribute.live-video.net  
demo1-ivs-metadata.demo1ivsmetadataoutputchannelplayback = https://XXXXXXXXXX.us-east-1.playback.live-video.net/api/video/v1/us-east-1.XXXXXXXXXX.channel.XXXXXXXXXX.m3u8  
demo1-ivs-metadata.demo1ivsmetadataoutputstreamkey = XXXXXXXXXX  
Stack ARN:  
arn:aws:cloudformation:us-east-1:XXXXXXXXXX:stack/demo1-ivs-metadata/XXXXXXXXXX  
  
✨  Total time: 15.72s
```

These are the information and credential that you can use to start hosting your live stream. Take note of these pieces of information, as we need them in the next steps.

Quick overview of these variables:

- `channelarn` is the ARN of your IVS channel. This is required to programmatically interact with your channel.
- `channelingest` is the variable to ingest video into your channel.
- `channelplayback` is the variable you need to pass into your website so it could play the video
- `streamkey` is the key to initiating a live stream. Remember to store your stream key in a safe place as anyone who has the stream key can ingest the video stream into the channel.

### Step 2: Ingest video with FFMPEG

In this step, we are going to ingest video into the IVS channel. Once we have successfully ingested video into IVS channel, our website can start playing the video using the playback URL.

![Ingest Video with FFMPEG](https://dsc.cloud/2c5cc2/ivs-3-2.png)

To ingest video into the IVS channel, you can use OBS, XSplit, or other broadcasting. In this tutorial, to simplify the demo, we are going to use FFMPEG to stream a video into the IVS channel.

#### **Configure Ingest Endpoint**

Before we run the FFMPEG command, we need to configure a few variables on `video-stream/stream-video.sh` file. You will need two variables: 1) `channelingest` and 2) `streamkey`, which you already obtained from deployment with CDK. Also, you need to have a video to ingest into your channel. A good starting point that you can use to test is "Big Buck Bunny" — which you can download from [peach.blender.org/download/](https://peach.blender.org/download/).

Once you have the required variables, you need to change the following lines on `video-stream/stream-video.sh`:

```bash
TEST_FILE="<YOUR_VIDEO_FILEPATH>.mp4"  
STREAM_URL="rtmps://<YOUR_CHANNEL_INGEST>:443/app/<YOUR_STREAM_KEY>"
```

#### **Run ffmpeg**

You also need to run the following command to make the script executable:

```bash
chmod +x stream-video.sh
```

To run the script, you can use the following command:

```bash
./stream-video.sh
```

And, it will start ingesting the video into your IVS channel.

### Step 3: Play Video Stream on Website

At this stage, we already have the IVS channel created and configured with the stream key, and ingesting the video into IVS channel. The next part is to run the website to host the live video stream. With IVS, you can host your live stream on a website or mobile application.

![Play video on website](https://dsc.cloud/2c5cc2/ivs-3-3.png)

In this tutorial, we use a website to host the live streaming by running the webserver on `localhost`. In practice, you will need to deploy the website so it can be accessible to your users. There are various ways to do this, two of my favorites approach are hosting it as a static website using AWS Amplify, or you can use Amazon ECS and AWS Fargate to host the dynamic website.

#### **Configure Variables**

The website needs to have the playback URL to identify where it should retrieve the video stream. To do this, get your playback URL variable from the CDK output and open `web/index.js`, and change the following line:

```javascript
var PLAYBACK_URL = "https://XXXXXXXXXX.us-east-1.playback.live-video.net/api/video/v1/us-east-1.XXXXXXXXXX.channel.XXXXXXXXXX.m3u8";
```

#### **Run Webserver**

Once you have your app configured, the next step is to run the webserver to host the web page.

Again, there are various ways to do this, and as we are working on Python, the easiest way is to run the following command to run the webserver:

```bash
cd web/  
python -m http.server 8080
```

Then, open your browser and point the URL to `http://localhost:8080`. You will find a page below and watch the video ingested from IVS playing.

![Demo Preview: Check live stream from web browser](https://dsc.cloud/2c5cc2/Screenshot+2022-04-14+at+11.13.23+PM+%281%29.png)

If you can see a similar page like this, Congrats! You just successfully integrate your IVS channel and your web page. If you don't, let me know in the comments below so I can help you out.

### Step 4: Sending Timed Metadata

In previous steps, we configured and ingested live video, and host a website to playback the video. These steps are the starting point for you to run a live streaming platform.

Now, we'd like to take it to the next level by adding an interactive element to your live stream. The key to interactivity is the ability to send out a message (or payload) into the live video stream. With IVS, you can send this message as timed metadata. In short, timed metadata is data about other data equipped with timestamps that are sent simultaneously to every viewer in any channel.

![Send Timed Metadata](https://dsc.cloud/2c5cc2/ivs-3-4.png)


The implementation of timed metadata isn't as complicated as its definition, and in this step, I'll explain how you can implement it.

#### Generator (App) Code Overview

The main application to send timed metadata into the IVS channel is located at `timed-metadata/app.py`. In this generator app, the payload we are going to send into the stream is a JSON data with the following structure:

```json
{  
    "current_time":"2022-Apr-14-153617",  
    "question":"Question : 1",  
           "answers": [  
                "True",  
                "False"  
            ]  
}
```

This is just an example of a payload and all payloads are populated by the following code:

```python
data = {  
            "current_time": datetime.datetime.utcnow().strftime('%Y-%b-%d-%H%M%S'),  
            "question": "Question : {}".format(i),  
            "answers": [  
                "True",  
                "False"  
            ]  
        }
```

Once we have the payload, we can send it to particular IVS channel by calling `put_metadata` API from IVS. Following lines show how to send metadata into IVS channel:

```python
response = ivs.put_metadata(  
                channelArn=channel_arn,  
                metadata=json.dumps(data)  
            )
```

Once the request is successfully sent, the stream will receive the following metadata:

```json
{  
  startTime: 3.458,  
  endTime: 3.458,  
  type: 'TextMetadataCue',  
  description: '',  
  text: '{"current_time": "2022-Apr-14-153617", "question": "Question : 8", "answers": ["True", "False"]}',  
   
}
```

With this data, you can build your interactive element by processing the metadata received on the `text` property.

#### Receiver (Web) Code Overview

In the previous step, we evaluated the generator app to send the timed metadata. We still need to receive the timed metadata on our web page. Let's review how we can instantiate the IVS player and how to receive the metadata.

The web page itself is built with 2 files: `web/index.html` and `web/index.js`. The main logic to consume the timed metadata is on `web/index.js`.

The first thing that you need to configure on this file is the `PLAYBACK_URL`. Simply replace the variable value with the value you obtained from CDK deployment outputs.

```javascript
var PLAYBACK_URL =  
    "https://XXXXXXXXXX.us-east-1.playback.live-video.net/api/video/v1/us-east-1.XXXXXXXXXX.channel.XXXXXXXXXX.m3u8";
```

As for the IVS player, I'm using VideoJS integration, and the following lines show how to instantiate the player:

```javascript
var player = videojs(  
    "amazon-ivs-videojs",  
    {  
      techOrder: ["AmazonIVS"],  
    },  
    () => {  
      player.src(PLAYBACK_URL);  
    }  
  );
```

To receive the timed metadata, we need to add `event_listener`.

```javascript
player.getIVSPlayer().addEventListener(PlayerEventType.TEXT_METADATA_CUE, fn_metadata);
```

This way, we invoke the `fn_metadata` function whenever the player receives timed metadata from IVS.

#### Sending Timed Metadata

To demonstrate sending metadata into the stream, you will need your IVS channel ARN. Once you have the channel ARN, you can pass it as input to the `timed-metadata/app.py` application.

To send the metadata, you can run this command:

```bash
python app.py arn:aws:ivs:us-east-1:XXXXXXXXXX:channel/XXXXXXXXXX
```

*To stop the application, you need to press `Ctrl-C`.*

Once you successfully run the application, it will send the metadata into the IVS channel and it will be received by the web page. If you go to the web page, it will show the timed metadata on the right column.

![Demo Preview: Website receives timed metadata](https://dsc.cloud/2c5cc2/screenshot-metadata+%281%29.png)

### What's Next?
You're probably wondering, what you can do with this metadata. This metadata is the raw form for adding interactivity. You can add pop-up dialog for running quizzes like the screenshot below using the metadata payload you configure from your application. 

![Demo Preview: Pop-up dialog](https://dsc.cloud/2c5cc2/Screenshot%202022-04-20%20at%207.53.34%20PM.png)


### Step 5: Cleaning up

Don't forget to remove all resources once you're done with the tutorial. To do cleanup, do the following steps:

```bash
cd cdk/  
cdk destroy
```

Choose "Yes" and CDK will remove all resources created.

--- 

## Wrapping Up

In conclusion, I found it's easy to run low-latency live streaming with Amazon IVS. Equipped with a timed metadata feature, it provides a simple way to add interactive elements with viewers and take your live streaming experience to the next level.

I hope this tutorial provides you with a good understanding of how Amazon IVS works. In the next post, I'll cover how to restrict playback access with authorization for a private channel.

Happy building!🤘🏻
— Donnie