paint-brush
How to Autoplay Streams When You Go Live by@amazonivs
569 reads
569 reads

How to Autoplay Streams When You Go Live

tldt arrow

Too Long; Didn't Read

Amazon Interactive Video Service (Amazon IVS) chat can be used to create an interactive experience. 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? Wouldn't it be better to autoplay the stream when it went live?
featured image - How to Autoplay Streams When You Go Live
Amazon Interactive Video Service (IVS)  HackerNoon profile picture

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.

Creating the Lambda Function and EventBridge Rule

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.

Listening For Custom Events on the Client Side

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.

Summary

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.