Skip to main content
Platform
Camera Kit Web

Amazon IVS Integration

Overview

Integrate the Camera Kit Web SDK seamlessly with Amazon IVS for an enhanced live streaming and interactive camera experience.

index.html

<html>
<head>
<title>Snap Camera Kit + Amazon IVS</title>
</head>
<body>
<div class="container">
<h1>Snap Camera Kit + Amazon IVS</h1>
<canvas id="canvas"></canvas>
<div class="settings">
<div class="item">
<label for="endpoint">Ingest Endpoint</label>
<input id="endpoint" placeholder="Enter your ingest endpoint" />
</div>
<div class="item">
<label for="key">Stream Key</label>
<input id="key" placeholder="Enter your stream key" />
</div>
<div class="item">
<label for="mics">Microphone</label>
<select id="mics"></select>
</div>
<div class="item">
<label for="cameras">Camera</label>
<select id="cameras"></select>
</div>

<div class="item">
<label for="lenses">Lens</label>
<select id="lenses"></select>
</div>
</div>
<div class="buttons">
<button disabled id="start">Start Broadcast</button>
<button disabled id="stop">Stop Broadcast</button>
</div>
</div>
<script type="module" src="./script.ts"></script>
</body>
</html>

script.ts

import IVSBroadcastClient from 'amazon-ivs-web-broadcast';
import {
bootstrapCameraKit,
CameraKitSession,
createMediaStreamSource,
Transform2D,
Lens,
} from '@snap/camera-kit';

// Element Selection

const liveRenderTarget = document.getElementById('canvas') as HTMLCanvasElement;
const lensSelect = document.getElementById('lenses') as HTMLSelectElement;
const cameraSelect = document.getElementById('cameras') as HTMLSelectElement;
const micSelect = document.getElementById('mics') as HTMLSelectElement;
const start = document.getElementById('start') as HTMLButtonElement;
const stop = document.getElementById('stop') as HTMLButtonElement;
const endpoint = document.getElementById('endpoint') as HTMLInputElement;
const key = document.getElementById('key') as HTMLInputElement;

// Camera Kit Setup

let mediaStream: MediaStream;

async function initCameraKit(): Promise<MediaStream> {
const cameraKit = await bootstrapCameraKit({
apiToken: '<API_TOKEN>',
});
const session = await cameraKit.createSession({ liveRenderTarget });
const { lenses } = await cameraKit.lensRepository.loadLensGroups([
'<LENS_GROUP_ID>',
]);

session.applyLens(lenses[0]);

attachCamerasToSelect(session);
attachLensesToSelect(lenses, session);

return liveRenderTarget.captureStream();
}

async function setCameraKitSource(session: CameraKitSession, deviceId: string) {
if (mediaStream) {
session.pause();
mediaStream.getVideoTracks()[0].stop();
}

mediaStream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId,
width: {
ideal: IVSBroadcastClient.BASIC_LANDSCAPE.maxResolution.width,
max: IVSBroadcastClient.BASIC_LANDSCAPE.maxResolution.width,
},
height: {
ideal: IVSBroadcastClient.BASIC_LANDSCAPE.maxResolution.height,
max: IVSBroadcastClient.BASIC_LANDSCAPE.maxResolution.height,
},
},
audio: false,
});

const source = createMediaStreamSource(mediaStream);

await session.setSource(source);

source.setTransform(Transform2D.MirrorX);

session.play();
}

async function attachCamerasToSelect(session: CameraKitSession) {
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(({ kind }) => kind === 'videoinput');

cameras.forEach((camera) => {
const option = document.createElement('option');

option.value = camera.deviceId;
option.text = camera.label;

cameraSelect.appendChild(option);
});

await setCameraKitSource(session, cameras[0].deviceId);

cameraSelect.addEventListener('change', (event) => {
const deviceId = (event.target as HTMLSelectElement).selectedOptions[0]
.value;

setCameraKitSource(session, deviceId);
});
}

async function attachLensesToSelect(lenses: Lens[], session: CameraKitSession) {
lenses.forEach((lens) => {
const option = document.createElement('option');

option.value = lens.id;
option.text = lens.name;

lensSelect.appendChild(option);
});

lensSelect.addEventListener('change', (event) => {
const lensId = (event.target as HTMLSelectElement).selectedOptions[0].value;
const lens = lenses.find((lens) => lens.id === lensId);

if (lens) session.applyLens(lens);
});
}

// Amazon IVS Setup

async function initAmazonIVS(videoMediaStream: MediaStream) {
const client = IVSBroadcastClient.create({
streamConfig: IVSBroadcastClient.BASIC_LANDSCAPE,
});

await attachAudioDevicesToSelect(client);

client.on(
IVSBroadcastClient.BroadcastClientEvents.ACTIVE_STATE_CHANGE,
// @ts-expect-error
(active: boolean) => {
start.disabled = active;
stop.disabled = !active;
}
);

client.addVideoInputDevice(videoMediaStream, 'camera1', { index: 0 });

start.addEventListener('click', () => startBroadcast(client));
stop.addEventListener('click', () => stopBroadcast(client));
key.addEventListener('input', handleAmazonConfigChange, true);
endpoint.addEventListener('input', handleAmazonConfigChange, true);
}

async function attachAudioDevicesToSelect(
client: IVSBroadcastClient.AmazonIVSBroadcastClient
) {
const devices = await navigator.mediaDevices.enumerateDevices();
const mics = devices.filter(({ kind }) => kind === 'audioinput');

mics.forEach((mic) => {
const option = document.createElement('option');

option.value = mic.deviceId;
option.text = mic.label;

micSelect.appendChild(option);
});

addAudioInputDevice(mics[0].deviceId);

micSelect.addEventListener('change', async (event) => {
const deviceId = (event.target as HTMLSelectElement).selectedOptions[0]
.value;

await addAudioInputDevice(deviceId);
});

async function addAudioInputDevice(deviceId: string) {
const audioMediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId,
},
});

client.addAudioInputDevice(audioMediaStream, 'mic1');
}
}

function handleAmazonConfigChange() {
start.disabled = !(key.value && endpoint.value);
}

function startBroadcast(client: IVSBroadcastClient.AmazonIVSBroadcastClient) {
client.startBroadcast(key.value, endpoint.value);
}

function stopBroadcast(client: IVSBroadcastClient.AmazonIVSBroadcastClient) {
client.stopBroadcast();
}

// Init

initCameraKit().then(initAmazonIVS);
Was this page helpful?
Yes
No