Sometimes it's the little features that differentiate one application from another. If you've added live streaming to your application and your user's navigate to a channel that they are interested in viewing, what happens if the channel is not currently broadcasting when they hit the page? Maybe you have added Amazon Interactive Video Service (Amazon IVS) chat to create an interactive experience so that viewers can spend the time waiting for a broadcast chatting with each other, but what happens when the broadcast goes live? Do your viewers have to click a 'play' button, or worse yet reload the entire web page to view the stream? How can we improve this experience? Wouldn't it be better to autoplay the stream when it goes live? Of course it would! Sadly, the Amazon IVS player does not have a built-in method for autoplaying live stream. But, we can work around this limitation!
One option to autoplay videos is to use polling (via setInterval()
on the client side) to check to see if the stream is broadcasting:
setInterval(() => {
ivsPlayer.load(streamUrl);
ivsPlayer.play();
});
This technically works, but it's not the best idea. With the polling approach, you would end up with tons of 404
errors in the console. Also, you'd have to store the interval and eventually clear it later on when the stream starts playing. How often should you run the check? Every second seems to frequent, every 10 seconds seems to infrequent. It's a messy approach, and requires extra code to manage.
A better approach would be to send a push notification to the client when a stream goes online. With this approach, you avoid the messiness of polling and can be assured that the stream starts as soon as possible after the broadcaster has gone online. So how do we know when a stream is online, and how can we easily send the notification to the client?
We've previously looked at using Amazon EventBridge rules to send push notifications or alerts when a stream goes online, and we can use a similar approach to autoplay live streams. With this approach, we'll create an EventBridge rule that triggers an AWS Lambda function when a stream goes online. But, instead of (or in addition to) sending a push notification, we can use Amazon IVS chat custom events to send a message to all connected chat clients and in the client-side handler start playing the stream when the message is received.
Not using Amazon IVS Chat? That's OK! You can utilize any pub/sub or WebSocket implementation to send the message to your front end. Also, even if you're not using Amazon IVS chat for interactive chat, you can still utilize it as an event bus for application messaging.
Let's set up a serverless application using AWS Serverless Application Model (AWS SAM). In this application, we'll have a function that will be used to send the custom chat event. Let's first look at the AWS Lambda function (index.mjs
). This function will need to use a layer that enables us to utilize the @aws-sdk/ivs-chat
module from the AWS SDK for JavaScript (v3). We'll use the SendEvent
(docs) method to send a custom event to our chat clients.
import { IvschatClient, CreateChatTokenCommand, SendEventCommand } from '@aws-sdk/client-ivschat';
const chatArn = process.env.DEMO_CHAT_ARN;
export const streamStarted = async (event) => {
const client = new IvschatClient();
const sendEventInput = {
roomIdentifier: chatArn,
eventName: 'STREAM_STARTED',
attributes: {
event: JSON.stringify(event),
},
}
const sendEventRequest = new SendEventCommand(sendEventInput);
const sendEventResponse = await client.send(sendEventRequest)
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS,GET,PUT,POST,DELETE',
'Content-Type': 'application/json'
},
isBase64Encoded: false
};
response.statusCode = 200;
response.body = JSON.stringify(sendEventResponse, '', 2);
return response;
};
We'll need a template.yaml
file to create and deploy the function. In this template, we'll also add a definition for the EventBridge rule and create the necessary permissions for the rule to invoke the AWS Lambda function. Also note that I'm setting the DEMO_CHAT_ARN
as an environment variable so that it can be accessed in the AWS Lambda function.
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Amazon IVS Functions
Globals:
Function:
Runtime: nodejs18.x
Timeout: 30
MemorySize: 128
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'Content-Type'"
AllowOrigin: "'*'"
MaxAge: "'600'"
Resources:
LambdaRefLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: sam-app-dependencies
Description: Dependencies for sam app
ContentUri: dependencies/
CompatibleRuntimes:
- nodejs18.x
LicenseInfo: "MIT"
RetentionPolicy: Retain
IVSAccessPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: IVSAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- 'ivschat:*'
Resource: '*'
Roles:
- Ref: StreamStartedRole
StreamStarted:
Type: 'AWS::Serverless::Function'
Properties:
Environment:
Variables:
DEMO_CHAT_ARN: 'arn:aws:ivschat:us-east-1:[redacted]:room/[redacted]'
Handler: index.streamStarted
Layers:
- !Ref LambdaRefLayer
CodeUri: lambda/
EventRule0:
Type: AWS::Events::Rule
Properties:
Description: >-
Rule to send a custom chat event when an Amazon IVS live stream session
begins
EventBusName: default
EventPattern:
source:
- aws.ivs
detail-type:
- IVS Stream State Change
detail:
event_name:
- Stream Start
Name: demo-stream-started-0
State: ENABLED
Targets:
-
Arn:
Fn::GetAtt:
- "StreamStarted"
- "Arn"
Id: "StreamStartedTarget"
PermissionForEventsToInvokeLambda:
Type: AWS::Lambda::Permission
Properties:
FunctionName:
Ref: "StreamStarted"
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn:
Fn::GetAtt:
- "EventRule0"
- "Arn"
Now we can package
this serverless application via the AWS SAM CLI.
Note: If you're not familiar with AWS SAM, check out the 'getting started' guide.
sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket ivs-demo
And deploy
it.
sam deploy --template-file packaged.yaml --stack-name ivs-demo-stack --capabilities CAPABILITY_IAM
Once deployed, you can verify that the EventBridge rule was created. One way to do that is to view your rules in the EventBridge console.
You can also verify that the AWS Lambda was created via the console.
Now we can start a broadcast on the Amazon IVS channel, and observe that the EventBridge rule has been invoked via the console.
Now that the we have custom events being published to an Amazon IVS chat room, we can set up our front-end to connect to the chat room and add some logic to our onmessage
handler to play the stream when the custom event is received. The AWS Lambda function above sends the triggering event in the Attributes
property of the custom event, so we can use the data from that triggering event to check the channel name and determine if we need to play the stream. The playStream()
method is not defined in the example below, but it would contain any necessary logic to play the stream depending on your chosen video player.
const connection = new WebSocket('[chat room endpoint]', '[chat token]');
connection.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.Type === 'MESSAGE') {
// render chat message
}
if (data.Type === 'EVENT') {
switch (data.EventName) {
case 'STREAM_STARTED':
const triggerEvent = JSON.parse(data.Attributes.event);
if (triggerEvent.detail.channel_name === 'demo-channel' && !isPlaying) {
playStream();
}
break;
}
}
}
And that's it! Our stream will now automatically play when a broadcaster goes online.
In this post, we learned how to use EventBridge rules to send a custom Amazon IVS chat event when an Amazon IVS live stream session begins so that the live stream video can be automatically played. Question, comments, and feedback is always welcomed below.
Also published here.