Publication et abonnement avec le SDK de diffusion Web IVS | Streaming en temps réel - Amazon IVS

Publication et abonnement avec le SDK de diffusion Web IVS | Streaming en temps réel

Ce document explique les étapes nécessaires pour la publication et l'abonnement à une étape à l'aide du SDK de diffusion Web IVS en temps réel.

Concepts

Trois concepts de base sous-tendent la fonctionnalité temps réel : scène, stratégie et événements. L’objectif de la conception consiste à minimiser la quantité de logique côté client nécessaire à la création d’un produit fonctionnel.

Étape

La classe Stage est le principal point d’interaction entre l’application hôte et le kit SDK. Il représente l’étape elle-même et est utilisé pour rejoindre et quitter l’étape. La création et la participation à une étape nécessitent une chaîne de jetons valide et non expirée provenant du plan de contrôle (représentée par token). Il est très facile de rejoindre une étape et de la quitter :

const stage = new Stage(token, strategy) try { await stage.join(); } catch (error) { // handle join exception } stage.leave();

Strategy

L’interface StageStrategy permet à l’application hôte de communiquer l’état de l’étape souhaité au kit SDK. Trois fonctions doivent être mises en œuvre : shouldSubscribeToParticipant, shouldPublishParticipant et stageStreamsToPublish. Elles sont toutes abordées ci-dessous.

Pour utiliser une stratégie définie, transmettez-la au constructeur Stage. Voici un exemple complet d’application utilisant une stratégie pour publier la webcam d’un participant sur l’étape et s’abonner à tous les participants. L’objectif de chaque fonction de stratégie requise est expliqué en détail dans les sections suivantes.

const devices = await navigator.mediaDevices.getUserMedia({ audio: true, video: { width: { max: 1280 }, height: { max: 720 }, } }); const myAudioTrack = new LocalStageStream(devices.getAudioTracks()[0]); const myVideoTrack = new LocalStageStream(devices.getVideoTracks()[0]); // Define the stage strategy, implementing required functions const strategy = { audioTrack: myAudioTrack, videoTrack: myVideoTrack, // optional updateTracks(newAudioTrack, newVideoTrack) { this.audioTrack = newAudioTrack; this.videoTrack = newVideoTrack; }, // required stageStreamsToPublish() { return [this.audioTrack, this.videoTrack]; }, // required shouldPublishParticipant(participant) { return true; }, // required shouldSubscribeToParticipant(participant) { return SubscribeType.AUDIO_VIDEO; } }; // Initialize the stage and start publishing const stage = new Stage(token, strategy); await stage.join(); // To update later (e.g. in an onClick event handler) strategy.updateTracks(myNewAudioTrack, myNewVideoTrack); stage.refreshStrategy();

Abonnement aux participants

shouldSubscribeToParticipant(participant: StageParticipantInfo): SubscribeType

Lorsqu’un participant distant rejoint l’étape, le kit SDK interroge l’application hôte sur l’état de l’abonnement souhaité pour ce participant. Les options sont NONE, AUDIO_ONLY et AUDIO_VIDEO. Lors d’un renvoi d’une valeur pour cette fonction, l’application hôte n’a pas à se soucier de l’état de publication, de l’état actuel de l’abonnement ou de l’état de la connexion de l’étape. Si AUDIO_VIDEO est renvoyé, le kit SDK attend que le participant distant effectue une publication avant de s’abonner, puis il met à jour l’application hôte en émettant des événements tout au long du processus.

Voici un exemple d’implémentation :

const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }

Il s’agit de l’implémentation complète de cette fonction pour une application hôte qui souhaite toujours que tous les participants se voient mutuellement, comme une application de chat vidéo.

Des implémentations plus avancées sont également possibles. Par exemple, supposons que l’application fournisse un attribut role lors de la création du jeton avec CreateParticipantToken. L’application pourrait utiliser la propriété attributes sur StageParticipantInfo pour s’abonner de manière sélective à des participants en fonction des attributs fournis par le serveur :

const strategy = { shouldSubscribeToParticipant(participant) { switch (participant.attributes.role) { case 'moderator': return SubscribeType.NONE; case 'guest': return SubscribeType.AUDIO_VIDEO; default: return SubscribeType.NONE; } } // . . . other strategies properties }

Elle peut servir à créer une scène où les modérateurs peuvent surveiller tous les invités sans être vus ou entendus. L’application hôte pourrait utiliser une logique métier supplémentaire pour permettre aux modérateurs de se voir mutuellement tout en restant invisible pour les invités.

Configuration d’abonnement aux participants

subscribeConfiguration(participant: StageParticipantInfo): SubscribeConfiguration

Lorsqu’un participant distant est abonné (voir Abonnement aux participants), le kit SDK interroge l’application hôte sur une configuration d’abonnement personnalisée pour ce participant. Cette configuration est facultative et permet à l’application hôte de contrôler certains aspects du comportement de l’abonné. Pour plus d’informations sur les options de configuration, consultez SubscribeConfiguration dans la documentation de référence du kit SDK.

Voici un exemple d’implémentation :

const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }

Cette implémentation met à jour le délai minimum de la mémoire tampon pour tous les participants abonnés à un préréglage de MEDIUM.

Comme pour shouldSubscribeToParticipant, des implémentations plus avancées sont possibles. ParticipantInfo peut être utilisé pour mettre à jour de manière sélective la configuration d’abonnement pour des participants spécifiques.

Nous recommandons d’utiliser les comportements par défaut. Spécifiez une configuration personnalisée uniquement si vous souhaitez modifier un comportement particulier.

Publication

shouldPublishParticipant(participant: StageParticipantInfo): boolean

Une fois connecté à l’étape, le kit SDK interroge l’application hôte pour savoir si un participant en particulier doit effectuer une publication. Elle n’est invoquée que pour les participants locaux autorisés à publier sur la base du jeton fourni.

Voici un exemple d’implémentation :

const strategy = { shouldPublishParticipant: (participant) => { return true; } // . . . other strategies properties }

Il s’agit d’une application de chat vidéo standard dans laquelle les utilisateurs souhaitent toujours publier. Ils peuvent désactiver et réactiver leur son et leur vidéo pour être instantanément masqués ou vus/entendus. (Ils peuvent également utiliser la fonction de publication/d’annulation de la publication, mais c’est beaucoup plus lent. Il est préférable de désactiver ou de réactiver le son dans les cas d’utilisation où il est souvent souhaitable de modifier la visibilité.)

Choix des flux à publier

stageStreamsToPublish(): LocalStageStream[];

Lors de la publication, cette fonction est utilisée pour déterminer les flux audio et vidéo à publier. Ce point est abordé plus en détail dans la section Publier un flux multimédia.

Mise à jour de la stratégie

La stratégie se veut dynamique : les valeurs renvoyées par n’importe laquelle des fonctions ci-dessus peuvent être modifiées à tout moment. Par exemple, si l’application hôte ne souhaite pas publier tant que l’utilisateur final n’a pas appuyé sur un bouton, vous pouvez renvoyer une variable depuis shouldPublishParticipant (quelque chose comme hasUserTappedPublishButton). Lorsque cette variable change en fonction d’une interaction de l’utilisateur final, appelez stage.refreshStrategy() pour signaler au kit SDK qu’il doit interroger la stratégie pour connaître les dernières valeurs, en appliquant uniquement les éléments qui ont changé. Si le kit SDK constate que la valeur shouldPublishParticipant a changé, il lance le processus de publication. Si les requêtes du kit SDK et toutes les fonctions renvoient la même valeur qu’auparavant, l’appel refreshStrategy ne modifie pas l’étape.

Si la valeur de retour de shouldSubscribeToParticipant passe de AUDIO_VIDEO à AUDIO_ONLY, le flux vidéo est supprimé pour tous les participants dont les valeurs renvoyées ont été modifiées, s’il existait déjà un flux vidéo.

En général, l’étape utilise la stratégie pour appliquer au mieux la différence entre les stratégies précédentes et actuelles, sans que l’application hôte n’ait à se soucier de tout l’état requis pour la gérer correctement. Pour cette raison, et pour réduire les frais, envisagez d’appeler stage.refreshStrategy(), car cela ne fait rien à moins que la stratégie ne change.

Événements

Une instance Stage est un émetteur d’événements. En utilisant stage.on(), l’état de l’étape est communiqué à l’application hôte. Les mises à jour de l’interface utilisateur de l’application hôte peuvent généralement être entièrement prises en charge par les événements. Les événements sont les suivants :

stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_SUBSCRIBE_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => {}) stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => {})

Pour la plupart de ces événements, les ParticipantInfo correspondantes sont fournies.

Les informations fournies par les événements ne devraient pas avoir d’impact sur les valeurs de retour de la stratégie. Par exemple, la valeur de retour de shouldSubscribeToParticipant ne devrait pas changer lors de l’appel de STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED. Si l’application hôte souhaite s’abonner à un participant en particulier, elle doit renvoyer le type d’abonnement souhaité, quel que soit l’état de publication de ce participant. Le kit SDK est chargé de s’assurer que l’état souhaité de la stratégie est appliqué au bon moment en fonction de l’état de l’étape.

Publier un flux multimédia

Les appareils locaux tels que les microphones et les caméras sont récupérés en suivant les mêmes étapes que celles décrites ci-dessus dans Récupérer un MediaStream depuis un périphérique. Dans l’exemple, nous utilisonsMediaStream pour créer une liste d’objets LocalStageStream utilisés à des fins de publication par le kit SDK :

try { // Get stream using steps outlined in document above const stream = await getMediaStreamFromDevice(); let streamsToPublish = stream.getTracks().map(track => { new LocalStageStream(track) }); // Create stage with strategy, or update existing strategy const strategy = { stageStreamsToPublish: () => streamsToPublish } }

Publier un partage d’écran

Les applications doivent souvent publier un partage d’écran en plus de la caméra Web de l’utilisateur. La publication d’un partage d’écran nécessite la création d’un jeton supplémentaire pour la scène, spécifiquement pour la publication du contenu multimédia du partage d’écran. Utilisez getDisplayMedia et limitez la résolution à un maximum de 720p. Ensuite, les étapes sont similaires à celles de la publication d’une caméra sur la scène.

// Invoke the following lines to get the screenshare's tracks const media = await navigator.mediaDevices.getDisplayMedia({ video: { width: { max: 1280, }, height: { max: 720, } } }); const screenshare = { videoStream: new LocalStageStream(media.getVideoTracks()[0]) }; const screenshareStrategy = { stageStreamsToPublish: () => { return [screenshare.videoStream]; }, shouldPublishParticipant: (participant) => { return true; }, shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } } const screenshareStage = new Stage(screenshareToken, screenshareStrategy); await screenshareStage.join();

Afficher et supprimer des participants

Une fois l’inscription terminée, vous recevez un tableau d’objets StageStream via l’événement STAGE_PARTICIPANT_STREAMS_ADDED. L’événement vous fournit également des informations sur les participants pour vous aider lors de l’affichage des flux de contenu multimédia :

stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { const streamsToDisplay = streams; if (participant.isLocal) { // Ensure to exclude local audio streams, otherwise echo will occur streamsToDisplay = streams.filter(stream => stream.streamType === StreamType.VIDEO) } // Create or find video element already available in your application const videoEl = getParticipantVideoElement(participant.id); // Attach the participants streams videoEl.srcObject = new MediaStream(); streamsToDisplay.forEach(stream => videoEl.srcObject.addTrack(stream.mediaStreamTrack)); })

Lorsqu’un participant arrête de publier ou en cas de désabonnement d’un flux, la fonction STAGE_PARTICIPANT_STREAMS_REMOVED est appelée avec les flux qui ont été supprimés. Les applications hôte doivent utiliser ce signal pour supprimer le flux vidéo du participant du DOM.

STAGE_PARTICIPANT_STREAMS_REMOVED est invoqué pour tous les scénarios dans lesquels un flux peut être supprimé, notamment :

  • Le participant distant arrête de publier.

  • Un appareil local se désabonne ou modifie l’abonnement de AUDIO_VIDEO en AUDIO_ONLY.

  • Le participant distant quitte l’étape.

  • Le participant local quitte l’étape.

Comme STAGE_PARTICIPANT_STREAMS_REMOVED est invoqué pour tous les scénarios, aucune logique métier personnalisée n’est requise pour supprimer des participants de l’interface utilisateur lors des opérations de départ locales ou à distance.

Désactiver et réactiver le son des flux de médias sociaux

Les objets LocalStageStream ont une fonction setMuted qui contrôle si le son du flux est désactivé. Cette fonction peut être appelée sur le flux avant ou après son renvoi par la fonction de stratégie stageStreamsToPublish.

Important : si une nouvelle instance d’objet LocalStageStream est renvoyée par stageStreamsToPublish après un appel à refreshStrategy, l’état muet du nouvel objet de flux est appliqué à l’étape. Lorsque vous créez des instances LocalStageStream, veillez à ce que l’état muet attendu soit conservé.

Surveiller l’état muet du contenu multimédia des participants distants

Lorsque les participants modifient l’état muet de leur vidéo ou audio, l’événement STAGE_STREAM_MUTE_CHANGED est déclenché avec une liste des flux qui ont changé. Utilisez la propriété isMuted sur StageStream pour mettre à jour votre interface utilisateur en conséquence :

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (stream.streamType === 'video' && stream.isMuted) { // handle UI changes for video track getting muted } })

Vous pouvez également consulter StageParticipantInfo pour obtenir des informations sur l’état indiquant si le son ou la vidéo est désactivé :

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (participant.videoStopped || participant.audioMuted) { // handle UI changes for either video or audio } })

Obtenir les statistiques WebRTC

Pour obtenir les dernières statistiques WebRTC relatives à un flux de publication ou d’abonnement, utilisez getStats sur StageStream. Il s’agit d’une méthode asynchrone avec laquelle vous pouvez récupérer des statistiques soit via une attente, soit en enchaînant une promesse. Le résultat est un RTCStatsReport, un dictionnaire contenant toutes les statistiques standard.

try { const stats = await stream.getStats(); } catch (error) { // Unable to retrieve stats }

Optimisation de contenu multimédia

Pour des performances optimales, il est recommandé de limiter les appels getUserMedia et getDisplayMedia aux contraintes suivantes :

const CONSTRAINTS = { video: { width: { ideal: 1280 }, // Note: flip width and height values if portrait is desired height: { ideal: 720 }, framerate: { ideal: 30 }, }, };

Vous pouvez restreindre davantage le contenu multimédia à l’aide d’options supplémentaires transmises au constructeur LocalStageStream :

const localStreamOptions = { minBitrate?: number; maxBitrate?: number; maxFramerate?: number; simulcast: { enabled: boolean } } const localStream = new LocalStageStream(track, localStreamOptions)

Dans le code ci-dessus :

  • minBitrate définit le débit minimum que le navigateur doit être censé utiliser. Toutefois, un flux vidéo de faible complexité peut pousser l’encodeur à descendre en dessous de ce débit.

  • maxBitrate définit un débit maximal que le navigateur ne doit pas dépasser pour ce flux.

  • maxFramerate définit une fréquence d’images maximale que le navigateur ne doit pas dépasser pour ce flux.

  • L’option simulcast n’est utilisable que sur les navigateurs basés sur Chromium. Elle permet d’envoyer trois couches de rendu du flux.

    • Cela permet au serveur de choisir le rendu à envoyer aux autres participants, en fonction des limites de leur réseau.

    • Lorsque simulcast est spécifié en même temps qu’une valeur maxBitrate et/ou maxFramerate, on s’attend à ce que la couche de rendu la plus élevée soit configurée en tenant compte de ces valeurs, à condition que maxBitrate ne descende pas en dessous de la valeur maxBitrate par défaut de 900 kbps de la deuxième couche la plus élevée du SDK interne.

    • Si maxBitrate est spécifié comme étant trop faible par rapport à la valeur par défaut de la deuxième couche la plus élevée, simulcast sera désactivé.

    • simulcast ne peut pas être activé et désactivé sans republier le média. Pour ce faire, il faut que shouldPublishParticipant renvoie la valeur false, que refreshStrategy soit appelé, que shouldPublishParticipant renvoie la valeur true et que refreshStrategy soit à nouveau appelé.

Obtenir les attributs des participants

Si vous spécifiez des attributs dans la demande de point de terminaison CreateParticipantToken, vous pouvez les voir dans les propriétés StageParticipantInfo :

stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => { console.log(`Participant ${participant.id} info:`, participant.attributes); })

Gestion des problèmes de réseau

En cas de perte de la connexion réseau de l’appareil local, le kit SDK essaie de se reconnecter en interne sans aucune action de l’utilisateur. Dans certains cas, le kit SDK échoue et une action de l’utilisateur est requise.

D’une manière générale, l’état de l’étape peut être géré via l’événement STAGE_CONNECTION_STATE_CHANGED :

stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => { switch (state) { case StageConnectionState.DISCONNECTED: // handle disconnected UI break; case StageConnectionState.CONNECTING: // handle establishing connection UI break; case StageConnectionState.CONNECTED: // SDK is connected to the Stage break; case StageConnectionState.ERRORED: // SDK encountered an error and lost its connection to the stage. Wait for CONNECTED. break; })

En général, vous pouvez ignorer un état d’erreur qui survient après avoir rejoint une scène avec succès, car le kit SDK tentera de se rétablir automatiquement. Si le kit SDK signale un état ERRORED et que la scène reste dans l’état CONNECTING pendant une période prolongée (par exemple, 30 secondes ou plus), cela peut indiquer une déconnexion du réseau.

Diffuser la scène sur un canal IVS

Pour diffuser une étape, créez une session IVSBroadcastClient distincte, puis suivez les instructions habituelles de diffusion avec le kit SDK, décrites ci-dessus. La liste des StageStream exposés via STAGE_PARTICIPANT_STREAMS_ADDED peut être utilisée pour récupérer les flux de contenu multimédia des participants, qui peuvent être appliqués à la composition du flux de diffusion, comme suit :

// Setup client with preferred settings const broadcastClient = getIvsBroadcastClient(); stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { streams.forEach(stream => { const inputStream = new MediaStream([stream.mediaStreamTrack]); switch (stream.streamType) { case StreamType.VIDEO: broadcastClient.addVideoInputDevice(inputStream, `video-${participant.id}`, { index: DESIRED_LAYER, width: MAX_WIDTH, height: MAX_HEIGHT }); break; case StreamType.AUDIO: broadcastClient.addAudioInputDevice(inputStream, `audio-${participant.id}`); break; } }) })

Vous pouvez éventuellement composer une scène et la diffuser sur un canal IVS à faible latence, afin de toucher un public plus large. Consultez la section Activation d’hôtes multiples sur un flux Amazon IVS dans le guide de l’utilisateur du streaming à faible latence IVS.