

# Utilisation de Snap avec le SDK de diffusion IVS
<a name="broadcast-3p-camera-filters-integrating-snap"></a>

Ce document explique comment utiliser le SDK Camera Kit de Snap avec le SDK de diffusion IVS.

## Web
<a name="integrating-snap-web"></a>

Cette section suppose que vous connaissez déjà la [diffusion et l’abonnement à des vidéos à l’aide du SDK de diffusion Web](getting-started-pub-sub-web.md).

Pour intégrer le SDK Camera Kit de Snap au SDK de diffusion Web en temps réel IVS, vous devez effectuer les actions suivantes :

1. Installez le SDK Camera Kit et le Webpack. (Notre exemple utilise Webpack comme bundler, mais vous pouvez utiliser n’importe quel bundler de votre choix.)

1. Créez vos `index.html`.

1. Ajoutez des éléments de configuration.

1. Créez vos `index.css`.

1. Affichez et configurez les participants.

1. Affichez les caméras et les microphones connectés.

1. Créez une session Camera Kit.

1. Récupérez les objectifs et remplissez le sélecteur d’objectifs.

1. Affichez le résultat d’une session Camera Kit sur un canevas.

1. Créez une fonction pour remplir la liste déroulante Lens.

1. Fournissez à Camera Kit une source multimédia pour le rendu et la diffusion d’un `LocalStageStream`.

1. Créez vos `package.json`.

1. Créez un fichier de configuration Webpack.

1. Configurez un serveur HTTPS et effectuez des tests.

Chacune de ces étapes est décrite ci-dessous.

### Installez le SDK et le Webpack du Camera Kit
<a name="integrating-snap-web-install-camera-kit"></a>

Dans cet exemple, nous utilisons Webpack comme bundler, mais vous pouvez utiliser n’importe quel bundler.

```
npm i @snap/camera-kit webpack webpack-cli
```

### Créez le fichier index.html
<a name="integrating-snap-web-create-index"></a>

Puis, créez le modèle de code HTML et importez le SDK de diffusion Web sous forme de balise de script. Dans le code suivant, veillez à remplacer `<SDK version>` par la version du SDK de diffusion que vous utilisez.

#### HTML
<a name="integrating-snap-web-create-index-code"></a>

```
<!--
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */
-->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

  <title>Amazon IVS Real-Time Streaming Web Sample (HTML and JavaScript)</title>

  <!-- Fonts and Styling -->
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.css" />
  <link rel="stylesheet" href="./index.css" />

  <!-- Stages in Broadcast SDK -->
  <script src="https://web-broadcast.live-video.net/<SDK version>/amazon-ivs-web-broadcast.js"></script>
</head>

<body>
  <!-- Introduction -->
  <header>
    <h1>Amazon IVS Real-Time Streaming Web Sample (HTML and JavaScript)</h1>

    <p>This sample is used to demonstrate basic HTML / JS usage. <b><a href="https://docs.aws.amazon.com/ivs/latest/LowLatencyUserGuide/multiple-hosts.html">Use the AWS CLI</a></b> to create a <b>Stage</b> and a corresponding <b>ParticipantToken</b>. Multiple participants can load this page and put in their own tokens. You can <b><a href="https://aws.github.io/amazon-ivs-web-broadcast/docs/sdk-guides/stages#glossary" target="_blank">read more about stages in our public docs.</a></b></p>
  </header>
  <hr />
  
  <!-- Setup Controls -->
 
  <!-- Display Local Participants -->
  
  <!-- Lens Selector -->

  <!-- Display Remote Participants -->

  <!-- Load All Desired Scripts -->
```

### Ajout d’éléments de configuration
<a name="integrating-snap-web-add-setup-elements"></a>

Créez le HTML pour la sélection d’une caméra, d’un microphone et d’un objectif et la spécification d’un jeton de participant :

#### HTML
<a name="integrating-snap-web-setup-controls-code"></a>

```
<!-- Setup Controls -->
  <div class="row">
    <div class="column">
      <label for="video-devices">Select Camera</label>
      <select disabled id="video-devices">
        <option selected disabled>Choose Option</option>
      </select>
    </div>
    <div class="column">
      <label for="audio-devices">Select Microphone</label>
      <select disabled id="audio-devices">
        <option selected disabled>Choose Option</option>
      </select>
    </div>
    <div class="column">
      <label for="token">Participant Token</label>
      <input type="text" id="token" name="token" />
    </div>
    <div class="column" style="display: flex; margin-top: 1.5rem">
      <button class="button" style="margin: auto; width: 100%" id="join-button">Join Stage</button>
    </div>
    <div class="column" style="display: flex; margin-top: 1.5rem">
      <button class="button" style="margin: auto; width: 100%" id="leave-button">Leave Stage</button>
    </div>
  </div>
```

Ajoutez du code HTML supplémentaire en dessous pour afficher les flux de caméra des participants locaux et distants :

#### HTML
<a name="integrating-snap-web-local-remote-participants-code"></a>

```
 <!-- Local Participant -->
<div class="row local-container">
    <canvas id="canvas"></canvas>

    <div class="column" id="local-media"></div>
    <div class="static-controls hidden" id="local-controls">
      <button class="button" id="mic-control">Mute Mic</button>
      <button class="button" id="camera-control">Mute Camera</button>
    </div>
  </div>

  
  <hr style="margin-top: 5rem"/>
  
  <!-- Remote Participants -->
  <div class="row">
    <div id="remote-media"></div>
  </div>
```

Chargez une logique supplémentaire, notamment des méthodes d’assistance pour configurer la caméra et le fichier JavaScript fourni. (Plus loin dans cette section, vous allez créer ces fichiers JavaScript et les regrouper en un seul fichier ; l’objectif est de pouvoir importer Camera Kit en tant que module. Le fichier JavaScript fourni contiendra la logique de configuration du Camera Kit, d’application d’un objectif et de diffusion du flux de caméra avec un objectif appliqué à une scène.) Ajoutez des balises de fermeture pour les éléments `body` et `html` afin de compléter la création de `index.html`.

#### HTML
<a name="integrating-snap-web-load-all-scripts-code"></a>

```
<!-- Load all Desired Scripts -->
  <script src="./helpers.js"></script>
  <script src="./media-devices.js"></script>
  <!-- <script type="module" src="./stages-simple.js"></script> -->
  <script src="./dist/bundle.js"></script>
</body>
</html>
```

### Créer le fichier index.css
<a name="integrating-snap-web-create-index-css"></a>

Créez un fichier source CSS pour styliser la page. Nous ne reviendrons pas sur ce code afin de nous concentrer sur la logique de gestion d’une scène et d’intégration avec le kit SDK de Snap.

#### CSS
<a name="integrating-snap-web-create-index-css-code"></a>

```
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */

html,
body {
  margin: 2rem;
  box-sizing: border-box;
  height: 100vh;
  max-height: 100vh;
  display: flex;
  flex-direction: column;
}

hr {
  margin: 1rem 0;
}

table {
  display: table;
}

canvas {
  margin-bottom: 1rem;
  background: green;
}

video {
  margin-bottom: 1rem;
  background: black;
  max-width: 100%;
  max-height: 150px;
}

.log {
  flex: none;
  height: 300px;
}

.content {
  flex: 1 0 auto;
}

.button {
  display: block;
  margin: 0 auto;
}

.local-container {
  position: relative;
}

.static-controls {
  position: absolute;
  margin-left: auto;
  margin-right: auto;
  left: 0;
  right: 0;
  bottom: -4rem;
  text-align: center;
}

.static-controls button {
  display: inline-block;
}

.hidden {
  display: none;
}

.participant-container {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  margin: 1rem;
}

video {
  border: 0.5rem solid #555;
  border-radius: 0.5rem;
}
.placeholder {
  background-color: #333333;
  display: flex;
  text-align: center;
  margin-bottom: 1rem;
}
.placeholder span {
  margin: auto;
  color: white;
}
#local-media {
  display: inline-block;
  width: 100vw;
}

#local-media video {
  max-height: 300px;
}

#remote-media {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row;
  width: 100%;
}

#lens-selector {
  width: 100%;
  margin-bottom: 1rem;
}
```

### Affichage et configuration des participants
<a name="integrating-snap-web-setup-participants"></a>

Ensuite, créez `helpers.js`. Il contient des méthodes d’assistance que vous utiliserez pour afficher et configurer les participants :

#### JavaScript
<a name="integrating-snap-web-setup-participants-code"></a>

```
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */

function setupParticipant({ isLocal, id }) {
  const groupId = isLocal ? 'local-media' : 'remote-media';
  const groupContainer = document.getElementById(groupId);

  const participantContainerId = isLocal ? 'local' : id;
  const participantContainer = createContainer(participantContainerId);
  const videoEl = createVideoEl(participantContainerId);

  participantContainer.appendChild(videoEl);
  groupContainer.appendChild(participantContainer);

  return videoEl;
}

function teardownParticipant({ isLocal, id }) {
  const groupId = isLocal ? 'local-media' : 'remote-media';
  const groupContainer = document.getElementById(groupId);
  const participantContainerId = isLocal ? 'local' : id;

  const participantDiv = document.getElementById(
    participantContainerId + '-container'
  );
  if (!participantDiv) {
    return;
  }
  groupContainer.removeChild(participantDiv);
}

function createVideoEl(id) {
  const videoEl = document.createElement('video');
  videoEl.id = id;
  videoEl.autoplay = true;
  videoEl.playsInline = true;
  videoEl.srcObject = new MediaStream();
  return videoEl;
}

function createContainer(id) {
  const participantContainer = document.createElement('div');
  participantContainer.classList = 'participant-container';
  participantContainer.id = id + '-container';

  return participantContainer;
}
```

### Affichage des caméras et des microphones connectés
<a name="integrating-snap-web-display-cameras-microphones"></a>

Ensuite, créez `media-devices.js`. Il contient des méthodes d’assistance pour afficher les caméras et les microphones connectés à votre appareil :

#### JavaScript
<a name="integrating-snap-web-display-cameras-microphones-code"></a>

```
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */

/**
 * Returns an initial list of devices populated on the page selects
 */
async function initializeDeviceSelect() {
  const videoSelectEl = document.getElementById('video-devices');
  videoSelectEl.disabled = false;

  const { videoDevices, audioDevices } = await getDevices();
  videoDevices.forEach((device, index) => {
    videoSelectEl.options[index] = new Option(device.label, device.deviceId);
  });

  const audioSelectEl = document.getElementById('audio-devices');

  audioSelectEl.disabled = false;
  audioDevices.forEach((device, index) => {
    audioSelectEl.options[index] = new Option(device.label, device.deviceId);
  });
}

/**
 * Returns all devices available on the current device
 */
async function getDevices() {
  // Prevents issues on Safari/FF so devices are not blank
  await navigator.mediaDevices.getUserMedia({ video: true, audio: true });

  const devices = await navigator.mediaDevices.enumerateDevices();
  // Get all video devices
  const videoDevices = devices.filter((d) => d.kind === 'videoinput');
  if (!videoDevices.length) {
    console.error('No video devices found.');
  }

  // Get all audio devices
  const audioDevices = devices.filter((d) => d.kind === 'audioinput');
  if (!audioDevices.length) {
    console.error('No audio devices found.');
  }

  return { videoDevices, audioDevices };
}

async function getCamera(deviceId) {
  // Use Max Width and Height
  return navigator.mediaDevices.getUserMedia({
    video: {
      deviceId: deviceId ? { exact: deviceId } : null,
    },
    audio: false,
  });
}

async function getMic(deviceId) {
  return navigator.mediaDevices.getUserMedia({
    video: false,
    audio: {
      deviceId: deviceId ? { exact: deviceId } : null,
    },
  });
}
```

### Création d’une session Camera Kit
<a name="integrating-snap-web-camera-kit-session"></a>

Créez `stages.js`. Il contient la logique permettant d’appliquer un objectif au flux de caméra et de diffuser le flux sur une scène. Nous vous recommandons de copier et de coller le bloc de code suivant dans `stages.js`. Vous pouvez ensuite revoir le code morceau par morceau pour comprendre ce qui se passe dans les sections suivantes.

#### JavaScript
<a name="integrating-snap-web-camera-kit-session-code"></a>

```
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */

const {
  Stage,
  LocalStageStream,
  SubscribeType,
  StageEvents,
  ConnectionState,
  StreamType,
} = IVSBroadcastClient;

import {
  bootstrapCameraKit,
  createMediaStreamSource,
  Transform2D,
} from '@snap/camera-kit';

let cameraButton = document.getElementById('camera-control');
let micButton = document.getElementById('mic-control');
let joinButton = document.getElementById('join-button');
let leaveButton = document.getElementById('leave-button');

let controls = document.getElementById('local-controls');
let videoDevicesList = document.getElementById('video-devices');
let audioDevicesList = document.getElementById('audio-devices');

let lensSelector = document.getElementById('lens-selector');
let session;
let availableLenses = [];

// Stage management
let stage;
let joining = false;
let connected = false;
let localCamera;
let localMic;
let cameraStageStream;
let micStageStream;

const liveRenderTarget = document.getElementById('canvas');

const init = async () => {
  await initializeDeviceSelect();

  const cameraKit = await bootstrapCameraKit({
    apiToken: 'INSERT_YOUR_API_TOKEN_HERE',
  });

  session = await cameraKit.createSession({ liveRenderTarget });
  const { lenses } = await cameraKit.lensRepository.loadLensGroups([
    'INSERT_YOUR_LENS_GROUP_ID_HERE',
  ]);

  availableLenses = lenses;
  populateLensSelector(lenses);

  const snapStream = liveRenderTarget.captureStream();

  lensSelector.addEventListener('change', handleLensChange);
  lensSelector.disabled = true;
  cameraButton.addEventListener('click', () => {
    const isMuted = !cameraStageStream.isMuted;
    cameraStageStream.setMuted(isMuted);
    cameraButton.innerText = isMuted ? 'Show Camera' : 'Hide Camera';
  });

  micButton.addEventListener('click', () => {
    const isMuted = !micStageStream.isMuted;
    micStageStream.setMuted(isMuted);
    micButton.innerText = isMuted ? 'Unmute Mic' : 'Mute Mic';
  });

  joinButton.addEventListener('click', () => {
    joinStage(session, snapStream);
  });

  leaveButton.addEventListener('click', () => {
    leaveStage();
  });
};

async function setCameraKitSource(session, mediaStream) {
  const source = createMediaStreamSource(mediaStream);
  await session.setSource(source);
  source.setTransform(Transform2D.MirrorX);
  session.play();
}

const populateLensSelector = (lenses) => {
  lensSelector.innerHTML = '<option selected disabled>Choose Lens</option>';

  lenses.forEach((lens, index) => {
    const option = document.createElement('option');
    option.value = index;
    option.text = lens.name || `Lens ${index + 1}`;
    lensSelector.appendChild(option);
  });
};

const handleLensChange = (event) => {
  const selectedIndex = parseInt(event.target.value);
  if (session && availableLenses[selectedIndex]) {
    session.applyLens(availableLenses[selectedIndex]);
  }
};

const joinStage = async (session, snapStream) => {
  if (connected || joining) {
    return;
  }
  joining = true;

  const token = document.getElementById('token').value;

  if (!token) {
    window.alert('Please enter a participant token');
    joining = false;
    return;
  }

  // Retrieve the User Media currently set on the page
  localCamera = await getCamera(videoDevicesList.value);
  localMic = await getMic(audioDevicesList.value);
  await setCameraKitSource(session, localCamera);

  // Create StageStreams for Audio and Video
  cameraStageStream = new LocalStageStream(snapStream.getVideoTracks()[0]);
  micStageStream = new LocalStageStream(localMic.getAudioTracks()[0]);

  const strategy = {
    stageStreamsToPublish() {
      return [cameraStageStream, micStageStream];
    },
    shouldPublishParticipant() {
      return true;
    },
    shouldSubscribeToParticipant() {
      return SubscribeType.AUDIO_VIDEO;
    },
  };

  stage = new Stage(token, strategy);

  // Other available events:
  // https://aws.github.io/amazon-ivs-web-broadcast/docs/sdk-guides/stages#events
  stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {
    connected = state === ConnectionState.CONNECTED;

    if (connected) {
      joining = false;
      controls.classList.remove('hidden');
      lensSelector.disabled = false;
    } else {
      controls.classList.add('hidden');
      lensSelector.disabled = true;
    }
  });

  stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {
    console.log('Participant Joined:', participant);
  });

  stage.on(
    StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED,
    (participant, streams) => {
      console.log('Participant Media Added: ', participant, streams);

      let streamsToDisplay = streams;

      if (participant.isLocal) {
        // Ensure to exclude local audio streams, otherwise echo will occur
        streamsToDisplay = streams.filter(
          (stream) => stream.streamType === StreamType.VIDEO
        );
      }

      const videoEl = setupParticipant(participant);
      streamsToDisplay.forEach((stream) =>
        videoEl.srcObject.addTrack(stream.mediaStreamTrack)
      );
    }
  );

  stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {
    console.log('Participant Left: ', participant);
    teardownParticipant(participant);
  });

  try {
    await stage.join();
  } catch (err) {
    joining = false;
    connected = false;
    console.error(err.message);
  }
};

const leaveStage = async () => {
  stage.leave();

  joining = false;
  connected = false;

  cameraButton.innerText = 'Hide Camera';
  micButton.innerText = 'Mute Mic';
  controls.classList.add('hidden');
};

init();
```

Pour la première partie de ce fichier, nous importons le SDK de diffusion et le SDK Web Camera Kit et initialisons les variables que nous utiliserons avec chaque SDK. Nous créons une session Camera Kit en appelant `createSession` après avoir démarré le [SDK Web Camera Kit](https://kit.snapchat.com/reference/CameraKit/web/0.7.0/index.html#bootstrapping-the-sdk). Notez qu’un objet d’élément de canevas est transmis à une session ; cela indique à Camera Kit qu’il doit s’afficher dans ce canevas.

#### JavaScript
<a name="integrating-snap-web-camera-kit-session-code-2"></a>

```
/*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */

const {
  Stage,
  LocalStageStream,
  SubscribeType,
  StageEvents,
  ConnectionState,
  StreamType,
} = IVSBroadcastClient;

import {
  bootstrapCameraKit,
  createMediaStreamSource,
  Transform2D,
} from '@snap/camera-kit';

let cameraButton = document.getElementById('camera-control');
let micButton = document.getElementById('mic-control');
let joinButton = document.getElementById('join-button');
let leaveButton = document.getElementById('leave-button');

let controls = document.getElementById('local-controls');
let videoDevicesList = document.getElementById('video-devices');
let audioDevicesList = document.getElementById('audio-devices');

let lensSelector = document.getElementById('lens-selector');
let session;
let availableLenses = [];

// Stage management
let stage;
let joining = false;
let connected = false;
let localCamera;
let localMic;
let cameraStageStream;
let micStageStream;

const liveRenderTarget = document.getElementById('canvas');

const init = async () => {
  await initializeDeviceSelect();

  const cameraKit = await bootstrapCameraKit({
    apiToken: 'INSERT_YOUR_API_TOKEN_HERE',
  });

  session = await cameraKit.createSession({ liveRenderTarget });
```

### Récupérer les objectifs et remplir le sélecteur d’objectifs
<a name="integrating-snap-web-fetch-apply-lens"></a>

Pour récupérer vos objectifs, remplacez l’espace réservé à l’ID du groupe d’objectifs par le vôtre, qui peut être trouvé sur le [portail des développeurs de Camera Kit](https://camera-kit.snapchat.com/). Remplissez la liste déroulante de sélection des objectifs à l’aide de la fonction `populateLensSelector()` que nous créerons plus tard.

#### JavaScript
<a name="integrating-snap-web-fetch-apply-lens-code"></a>

```
session = await cameraKit.createSession({ liveRenderTarget });
  const { lenses } = await cameraKit.lensRepository.loadLensGroups([
    'INSERT_YOUR_LENS_GROUP_ID_HERE',
  ]);

  availableLenses = lenses;
  populateLensSelector(lenses);
```

### Affichage de la sortie d’une session Camera Kit sur un canevas
<a name="integrating-snap-web-render-output-to-canvas"></a>

Vous pouvez utiliser la méthode [CaptureStream](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/captureStream) pour renvoyer une partie du contenu `MediaStream` du canevas. Avec un objectif appliqué, le canevas contiendra un flux vidéo provenant de la caméra. Ajoutez également des écouteurs d’événements pour les boutons qui permettent de désactiver la caméra et le microphone, ainsi que des écouteurs d’événements pour rejoindre et quitter une scène. Dans l’écouteur d’événements pour rejoindre une scène, nous faisons passer une session Camera Kit avec le `MediaStream` depuis le canevas, elle peut ainsi être diffusée sur une scène.

#### JavaScript
<a name="integrating-snap-web-render-output-to-canvas-code"></a>

```
const snapStream = liveRenderTarget.captureStream();

  lensSelector.addEventListener('change', handleLensChange);
  lensSelector.disabled = true;
  cameraButton.addEventListener('click', () => {
    const isMuted = !cameraStageStream.isMuted;
    cameraStageStream.setMuted(isMuted);
    cameraButton.innerText = isMuted ? 'Show Camera' : 'Hide Camera';
  });

  micButton.addEventListener('click', () => {
    const isMuted = !micStageStream.isMuted;
    micStageStream.setMuted(isMuted);
    micButton.innerText = isMuted ? 'Unmute Mic' : 'Mute Mic';
  });

  joinButton.addEventListener('click', () => {
    joinStage(session, snapStream);
  });

  leaveButton.addEventListener('click', () => {
    leaveStage();
  });
};
```

### Créer une fonction pour remplir la liste déroulante de l’objectif
<a name="integrating-snap-web-populate-lens-dropdown"></a>

Créez la fonction suivante pour remplir le sélecteur **Lens** avec les objectifs récupérés précédemment. Le sélecteur **Lens** est un élément de l’interface utilisateur de la page qui vous permet de choisir parmi une liste d’objectifs à appliquer au flux de la caméra. Créez également la fonction de rappel `handleLensChange` pour appliquer l’objectif spécifié lorsqu’il est sélectionné dans la liste déroulante **Lens**.

#### JavaScript
<a name="integrating-snap-web-populate-lens-dropdown-code"></a>

```
const populateLensSelector = (lenses) => {
  lensSelector.innerHTML = '<option selected disabled>Choose Lens</option>';

  lenses.forEach((lens, index) => {
    const option = document.createElement('option');
    option.value = index;
    option.text = lens.name || `Lens ${index + 1}`;
    lensSelector.appendChild(option);
  });
};

const handleLensChange = (event) => {
  const selectedIndex = parseInt(event.target.value);
  if (session && availableLenses[selectedIndex]) {
    session.applyLens(availableLenses[selectedIndex]);
  }
};
```

### Fournissez à Camera Kit une source multimédia pour le rendu et la diffusion d’un LocalStageStream
<a name="integrating-snap-web-publish-localstagestream"></a>

Pour diffuser un flux vidéo auquel un objectif est appliqué, créez une fonction appelée `setCameraKitSource` pour transmettre le `MediaStream` capturé précédemment sur le canevas. La `MediaStream` du canvas ne fonctionne pas pour le moment, car nous n’avons pas encore intégré notre flux de caméra local. Nous pouvons intégrer notre flux de caméra local en appelant la méthode d’assistance `getCamera` et en l’affectant à `localCamera`. Nous pouvons ensuite transmettre notre flux de caméra local (via `localCamera`) ainsi que l’objet de session à `setCameraKitSource`. La fonction `setCameraKitSource` convertit notre flux de caméra local en une [source multimédia pour CameraKit en appelant `createMediaStreamSource`](https://docs.snap.com/camera-kit/integrate-sdk/web/web-configuration#creating-a-camerakitsource). La source multimédia pour `CameraKit` est ensuite [transformée](https://docs.snap.com/camera-kit/integrate-sdk/web/web-configuration#2d-transforms) pour refléter la caméra frontale. L’effet d’objectif est ensuite appliqué à la source multimédia et rendu sur le canevas de sortie en appelant `session.play()`.

Avec l’objectif appliqué au `MediaStream` capturé depuis le canevas, nous pouvons ensuite procéder à sa diffusion sur une scène. Pour ce faire, nous créons un `LocalStageStream` avec les pistes vidéo du `MediaStream`. Une instance de `LocalStageStream` peut ensuite être transmise à un `StageStrategy` pour être diffusée.

#### JavaScript
<a name="integrating-snap-web-publish-localstagestream-code"></a>

```
async function setCameraKitSource(session, mediaStream) {
  const source = createMediaStreamSource(mediaStream);
  await session.setSource(source);
  source.setTransform(Transform2D.MirrorX);
  session.play();
}

const joinStage = async (session, snapStream) => {
  if (connected || joining) {
    return;
  }
  joining = true;

  const token = document.getElementById('token').value;

  if (!token) {
    window.alert('Please enter a participant token');
    joining = false;
    return;
  }

  // Retrieve the User Media currently set on the page
  localCamera = await getCamera(videoDevicesList.value);
  localMic = await getMic(audioDevicesList.value);
  await setCameraKitSource(session, localCamera);
  // Create StageStreams for Audio and Video
  // cameraStageStream = new LocalStageStream(localCamera.getVideoTracks()[0]);
  cameraStageStream = new LocalStageStream(snapStream.getVideoTracks()[0]);
  micStageStream = new LocalStageStream(localMic.getAudioTracks()[0]);

  const strategy = {
    stageStreamsToPublish() {
      return [cameraStageStream, micStageStream];
    },
    shouldPublishParticipant() {
      return true;
    },
    shouldSubscribeToParticipant() {
      return SubscribeType.AUDIO_VIDEO;
    },
  };
```

Le code restant ci-dessous concerne la création et la gestion de notre scène :

#### JavaScript
<a name="integrating-snap-web-create-manage-stage-code"></a>

```
stage = new Stage(token, strategy);

  // Other available events:
  // https://aws.github.io/amazon-ivs-web-broadcast/docs/sdk-guides/stages#events

  stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {
    connected = state === ConnectionState.CONNECTED;

    if (connected) {
      joining = false;
      controls.classList.remove('hidden');
    } else {
      controls.classList.add('hidden');
    }
  });

  stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {
    console.log('Participant Joined:', participant);
  });

  stage.on(
    StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED,
    (participant, streams) => {
      console.log('Participant Media Added: ', participant, streams);

      let streamsToDisplay = streams;

      if (participant.isLocal) {
        // Ensure to exclude local audio streams, otherwise echo will occur
        streamsToDisplay = streams.filter(
          (stream) => stream.streamType === StreamType.VIDEO
        );
      }

      const videoEl = setupParticipant(participant);
      streamsToDisplay.forEach((stream) =>
        videoEl.srcObject.addTrack(stream.mediaStreamTrack)
      );
    }
  );

  stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {
    console.log('Participant Left: ', participant);
    teardownParticipant(participant);
  });

  try {
    await stage.join();
  } catch (err) {
    joining = false;
    connected = false;
    console.error(err.message);
  }
};

const leaveStage = async () => {
  stage.leave();

  joining = false;
  connected = false;

  cameraButton.innerText = 'Hide Camera';
  micButton.innerText = 'Mute Mic';
  controls.classList.add('hidden');
};

init();
```

### Créer le fichier package.json
<a name="integrating-snap-web-package-json"></a>

Créez `package.json` et ajoutez la configuration JSON suivante. Ce fichier définit nos dépendances et inclut une commande de script pour générer une solution groupée de notre code.

#### Configuration JSON
<a name="integrating-snap-web-package-json-code"></a>

```
{
  "dependencies": {
    "@snap/camera-kit": "^0.10.0"
  },
  "name": "ivs-stages-with-snap-camerakit",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "webpack": "^5.95.0",
    "webpack-cli": "^5.1.4"
  }
}
```

### Création d’un fichier de configuration Webpack
<a name="integrating-snap-web-webpack-config"></a>

Créez `webpack.config.js` et ajoutez le code suivant. Ceci regroupe le code que nous avons créé jusqu’à présent afin que nous puissions utiliser l’instruction d’importation pour utiliser Camera Kit.

#### JavaScript
<a name="integrating-snap-web-webpack-config-code"></a>

```
const path = require('path');
module.exports = {
  entry: ['./stage.js'],
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};
```

Enfin, exécutez `npm run build` pour regrouper votre JavaScript tel que défini dans le fichier de configuration du Webpack. À des fins de test, vous pouvez ensuite servir du HTML et du JavaScript à partir de votre ordinateur local. Dans cet exemple, nous utilisons le module `http.server` de Python. 

### Configurer un serveur HTTPS et effectuer des tests
<a name="integrating-snap-web-https-server-test"></a>

Pour tester notre code, nous devons configurer un serveur HTTPS. L’utilisation d’un serveur HTTPS pour le développement local et le test de l’intégration de votre application web avec le kit SDK de la caméra Snap permettra d’éviter les problèmes CORS (Cross-Origin Resource Sharing).

Ouvrez un terminal et accédez au répertoire dans lequel vous avez créé tout le code jusqu’à présent. Exécutez la commande suivante pour générer un certificat SSL/TLS auto-signé et une clé privée :

```
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
```

Cette commande crée deux fichiers : `key.pem` (la clé privée) et `cert.pem` (le certificat auto-signé). Créez un nouveau fichier Python nommé `https_server.py` et ajoutez le code suivant :

#### Python
<a name="integrating-snap-web-https-server-test-code"></a>

```
import http.server
import ssl

# Set the directory to serve files from
DIRECTORY = '.'

# Create the HTTPS server
server_address = ('', 4443)
httpd = http.server.HTTPServer(
    server_address, http.server.SimpleHTTPRequestHandler)

# Wrap the socket with SSL/TLS
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)

print(f'Starting HTTPS server on https://localhost:4443, serving {DIRECTORY}')
httpd.serve_forever()
```

Ouvrez un terminal, accédez au répertoire dans lequel vous avez créé le fichier `https_server.py` et exécutez la commande suivante :

```
python3 https_server.py
```

Ceci démarre le serveur HTTPS sur https://localhost:4443, en servant les fichiers du répertoire actuel. Assurez-vous que les fichiers `cert.pem` et `key.pem` se trouvent dans le même répertoire que le fichier `https_server.py`.

Ouvrez votre navigateur et rendez-vous sur https://localhost:4443. Comme il s’agit d’un certificat SSL/TLS auto-signé, votre navigateur web ne lui fera pas confiance et vous recevrez un avertissement. Comme il s’agit uniquement d’un test, vous pouvez ignorer l’avertissement. Vous devriez alors voir l’effet de réalité augmentée du filtre que vous avez choisi s’appliquer à l’image de votre caméra sur l’écran.

Notez que cette configuration utilisant les modules `http.server` et `ssl` intégrés à Python convient au développement local et aux tests, mais n’est pas recommandée pour un environnement de production. Le certificat SSL/TLS auto-signé utilisé dans cette configuration n’est pas reconnu par les navigateurs Web et les autres clients, ce qui signifie que les utilisateurs rencontreront des avertissements de sécurité lorsqu’ils accèderont au serveur. De plus, bien que nous utilisions les modules intégrés http.server et ssl de Python dans cet exemple, vous pouvez choisir d’utiliser une autre solution de serveur HTTPS.

## Android
<a name="integrating-snap-android"></a>

Pour intégrer le SDK Camera Kit de Snap au SDK de diffusion Android IVS, vous devez installer le SDK Camera Kit, initialiser une session Camera Kit, appliquer un objectif puis transmettre la sortie de la session Camera Kit à la source d’entrée d’image personnalisée.

Pour installer le SDK Camera Kit, ajoutez ce qui suit au fichier `build.gradle` de votre module. Remplacez `$cameraKitVersion` par la [dernière version du SDK Camera Kit](https://docs.snap.com/camera-kit/integrate-sdk/mobile/changelog-mobile).

### Java
<a name="integrating-snap-android-install-camerakit-sdk-code"></a>

```
implementation "com.snap.camerakit:camerakit:$cameraKitVersion"
```

Initialisez et obtenez un `cameraKitSession`. Camera Kit fournit également un encapsuleur pratique pour les API [CameraX](https://developer.android.com/media/camera/camerax) d’Android, ainsi, vous n’avez pas à écrire de logique compliquée pour utiliser CameraX avec Camera Kit. Vous pouvez utiliser l’objet `CameraXImageProcessorSource` comme [source](https://snapchat.github.io/camera-kit-reference/api/android/latest/-camera-kit/com.snap.camerakit/-source/index.html) pour [ImageProcessor](https://snapchat.github.io/camera-kit-reference/api/android/latest/-camera-kit/com.snap.camerakit/-image-processor/index.html), cela vous permet de démarrer le flux d’images en mode caméra.

### Java
<a name="integrating-snap-android-initialize-camerakitsession-code"></a>

```
 protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        // Camera Kit support implementation of ImageProcessor that is backed by CameraX library:
        // https://developer.android.com/training/camerax
        CameraXImageProcessorSource imageProcessorSource = new CameraXImageProcessorSource( 
            this /*context*/, this /*lifecycleOwner*/
        );
        imageProcessorSource.startPreview(true /*cameraFacingFront*/);

        cameraKitSession = Sessions.newBuilder(this)
                .imageProcessorSource(imageProcessorSource)
                .attachTo(findViewById(R.id.camerakit_stub))
                .build();
    }
```

### Récupération et application des lentilles
<a name="integrating-snap-android-fetch-apply-lenses"></a>

Vous pouvez configurer les objectifs et les organiser dans le carrousel du [Portail des développeurs Camera Kit](https://camera-kit.snapchat.com/) :

#### Java
<a name="integrating-snap-android-configure-lenses-code"></a>

```
// Fetch lenses from repository and apply them
 // Replace LENS_GROUP_ID with Lens Group ID from https://camera-kit.snapchat.com
cameraKitSession.getLenses().getRepository().get(new Available(LENS_GROUP_ID), available -> {
     Log.d(TAG, "Available lenses: " + available);
     Lenses.whenHasFirst(available, lens -> cameraKitSession.getLenses().getProcessor().apply(lens, result -> {
          Log.d(TAG,  "Apply lens [" + lens + "] success: " + result);
      }));
});
```

Pour diffuser, envoyez des images traitées vers le `Surface` sous-jacent d’une source d’image personnalisée. Utilisez un objet `DeviceDiscovery` et créez une `CustomImageSource` pour renvoyer une `SurfaceSource`. Vous pouvez ensuite afficher le résultat d’une session `CameraKit` sur la `Surface` sous-jacente, fournie par une `SurfaceSource`.

#### Java
<a name="integrating-snap-android-broadcast-code"></a>

```
val publishStreams = ArrayList<LocalStageStream>()

val deviceDiscovery = DeviceDiscovery(applicationContext)
val customSource = deviceDiscovery.createImageInputSource(BroadcastConfiguration.Vec2(720f, 1280f))

cameraKitSession.processor.connectOutput(outputFrom(customSource.inputSurface))
val customStream = ImageLocalStageStream(customSource)

// After rendering the output from a Camera Kit session to the Surface, you can 
// then return it as a LocalStageStream to be published by the Broadcast SDK
val customStream: ImageLocalStageStream = ImageLocalStageStream(surfaceSource)
publishStreams.add(customStream)

@Override
fun stageStreamsToPublishForParticipant(stage: Stage, participantInfo: ParticipantInfo): List<LocalStageStream> = publishStreams
```