Skip to content

Instantly share code, notes, and snippets.

@recursivecodes
Last active July 26, 2023 21:45
Show Gist options
  • Save recursivecodes/1c264e0928f16c4b40445595ab28684b to your computer and use it in GitHub Desktop.
Save recursivecodes/1c264e0928f16c4b40445595ab28684b to your computer and use it in GitHub Desktop.

Prerequisites

Just to make sure everything works as expected, here are my current Node/AWS CLI versions (in case we need to look up an ARN or something with the CLI)

Node Version

$ node --version
v18.12.1

AWS CLI Version

$ aws --version
aws-cli/2.9.12 Python/3.9.11 Darwin/21.6.0 exe/x86_64 prompt/off

Node HTTP Server

To run the demos that we'll build, install http-server.

npm install -g http-server

Creating an IVS Channel

First, create an node project:

$ npm init

Next, install the IVS SDK:

$ npm install @aws-sdk/client-ivs

Reference doc.

create-channel.js

import { IvsClient, CreateChannelCommand } from "@aws-sdk/client-ivs";

const client = new IvsClient();

const input = {
  'name': 'demo-1',
  'latencyMode': 'LOW',
  'type': 'BASIC',
};
const command = new CreateChannelCommand(input);

const response = await client.send(command);
console.log(response);

Run:

node create-channel.js

Expected output:

{
  "$metadata": {
    "httpStatusCode": 200,
    "requestId": "[redacted]",
    "cfId": "[redacted]",
    "attempts": 1,
    "totalRetryDelay": 0
  },
  "channel": {
    "arn": "arn:aws:ivs:us-east-1:[redacted]:channel/9WeUUczacjIX",
    "authorized": false,
    "ingestEndpoint": "[redacted].global-contribute.live-video.net",
    "latencyMode": "LOW",
    "name": "demo-1",
    "playbackUrl": "https://f99084460c35.us-east-1.playback.live-video.net/api/video/v1/us-east-1.[redacted].channel.[redacted].m3u8",
    "recordingConfigurationArn": "",
    "tags": {},
    "type": "STANDARD"
  },
  "streamKey": {
    "arn": "arn:aws:ivs:us-east-1:[redacted]:stream-key/[redacted]",
    "channelArn": "arn:aws:ivs:us-east-1:[redacted]:channel/[redacted]",
    "tags": {},
    "value": "sk_us-east-[redacted]"
  }
}

Web Broadcasting

Cheat mode: https://stream.ivs.rocks

Reference docs.

Create web-broadcast.html.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>IVS Web Broadcast SDK</title>
  <script src="https://web-broadcast.live-video.net/1.3.3/amazon-ivs-web-broadcast.js"></script>
  <script>
  </script>
</head>

<body>
  <div>
    <p>
      <input type="password" id="streamKey" placeholder="Stream Key" />
    </p>
  </div>
  <div>
    <canvas id="preview" style="width: 800px; height: 450px;"></canvas>
  </div>
  <div>
    <p>
      <button type="button" id="toggle-btn">Toggle Broadcast</button>
    </p>
  </div>
</body>

</html>

Add a DOMContentLoaded listener:

document.addEventListener('DOMContentLoaded', async () => {
  window.broadcasting = false;
  // get permission to access mic/cam
  await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
});

The rest of the commands below will be added to the DOMContentLoaded handler.

Create an instance of the web broadcast client:

// create a broadcast client
const broadcastClient = IVSBroadcastClient.create({
  streamConfig: IVSBroadcastClient.BASIC_FULL_HD_LANDSCAPE,
  ingestEndpoint: 'f99084460c35.global-contribute.live-video.net',
});

Add a preview:

// add a preview 
const previewEl = document.getElementById('preview');
broadcastClient.attachPreview(previewEl);

Get mics/cams and create streams:

// get mics/cams 
// can also use this to populate a <select> 
// to allow user to choose mic/cam
const devices = await navigator.mediaDevices.enumerateDevices();
window.videoDevices = devices.filter(d => d.kind === 'videoinput');
window.audioDevices = devices.filter(d => d.kind === 'audioinput');

// get a stream from the webcam
window.cameraStream = await navigator.mediaDevices.getUserMedia({
  video: {
    deviceId: { exact: window.videoDevices[0].deviceId },
    width: { ideal: 1920, max: 1920 },
    height: { ideal: 1080, max: 1080 },
  },
});

// get a stream from the mic
window.microphoneStream = await navigator.mediaDevices.getUserMedia({
  audio: { deviceId: window.audioDevices[0].deviceId },
});

Add streams to client:

// add the streams to the client
broadcastClient.addVideoInputDevice(window.cameraStream, 'camera1', { index: 0 });
broadcastClient.addAudioInputDevice(window.microphoneStream, 'mic1');

Add button listener to toggle broadcast:

document.getElementById('toggle-btn').addEventListener('click', (evt) => {
  if (!window.broadcasting) {
    const streamKey = document.getElementById('streamKey').value;
    broadcastClient
      .startBroadcast(streamKey)
      .then((result) => {
        console.log('Broadcast started!');
        window.broadcasting = true;
      })
      .catch((error) => {
        console.error('Broadcast failed!', error);
        window.broadcasting = false;
      });
  }
  else {
    broadcastClient.stopBroadcast();
    console.log('Broadcast ended!');
    window.broadcasting = false;
  }
});

Playback

Cheat mode: https://debug.ivsdemos.com/ or Amazon IVS console.

Reference User Guide & API Docs.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>IVS Playback</title>
  <script src="https://player.live-video.net/1.19.0/amazon-ivs-player.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      // set stream url
      const streamUrl = '[Playback URL]';
      // init and play
      const ivsPlayer = IVSPlayer.create();
      ivsPlayer.attachHTMLVideoElement(document.getElementById('video-player'));
      ivsPlayer.load(streamUrl);
      ivsPlayer.play();
    });
  </script>
</head>

<body>
  <video id="video-player" style="height: 450px; width: 800px;" controls autoplay muted></video>
</body>

</html>

Stretch Goal

Add listeners for player events:

// listen for playing
ivsPlayer.addEventListener(IVSPlayer.PlayerState.PLAYING, () => {
  console.log('playing');
});
// listen for stopped
ivsPlayer.addEventListener(IVSPlayer.PlayerState.ENDED, () => {
  console.log('stopped');
});
// listen for metadata
ivsPlayer.addEventListener(IVSPlayer.PlayerEventType.TEXT_METADATA_CUE, (evt) => {
  console.log(evt);
});
// get quality, latency, buffer
setInterval(() => {
  console.log('Quality', ivsPlayer.getQuality());
  console.log(`Bitrate: ${ivsPlayer.getQuality().bitrate / 1000} kbps`);
  console.log(`Live Latency: ${ivsPlayer.getLiveLatency()} seconds`);
  console.log(`Buffer: ${ivsPlayer.getBufferDuration()} seconds`);
}, 1500);

Learn More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment