Publication et abonnement avec le SDK de diffusion Android 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 Android IVS en temps réel.
Concepts
Trois concepts de base sous-tendent la fonctionnalité temps réel : scène, stratégie et moteur de rendu. 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.
Stage stage = new Stage(context, token, strategy); try { stage.join(); } catch (BroadcastException exception) { // handle join exception } stage.leave();
La classe Stage
est également l’endroit où vous pouvez joindre le StageRenderer
:
stage.addRenderer(renderer); // multiple renderers can be added
Strategy
L’interface Stage.Strategy
permet à l’application hôte de communiquer l’état de l’étape souhaité au kit SDK. Trois fonctions doivent être mises en œuvre : shouldSubscribeToParticipant
, shouldPublishFromParticipant
et stageStreamsToPublishForParticipant
. Elles sont toutes abordées ci-dessous.
Abonnement aux participants
Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
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 par le biais du moteur de rendu tout au long du processus.
Voici un exemple d’implémentation :
@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; }
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. Utilisez la propriété userInfo
sur ParticipantInfo
pour vous abonner de manière sélective aux participants en fonction des attributs fournis par le serveur :
@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { switch(participantInfo.userInfo.get(“role”)) { case “moderator”: return Stage.SubscribeType.NONE; case “guest”: return Stage.SubscribeType.AUDIO_VIDEO; default: return Stage.SubscribeType.NONE; } }
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 subscribeConfigurationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
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
Voici un exemple d’implémentation :
@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM()); return config; }
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
boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
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 :
@Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; }
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
@Override List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); }
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 shouldPublishFromParticipant
(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 shouldPublishFromParticipant
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
n’apportera aucune modification à l’étape.
Si la valeur de retour de shouldSubscribeToParticipant
passe de AUDIO_VIDEO
à AUDIO_ONLY
, le flux vidéo sera 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.
Moteur de rendu
L’interface StageRenderer
communique l’état de l’étape à l’application hôte. Les mises à jour de l’interface utilisateur de l’application hôte peuvent généralement être entièrement optimisées par les événements fournis par le moteur de rendu. Le moteur de rendu fournit les fonctions suivantes :
void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantLeft(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantPublishStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.PublishState publishState); void onParticipantSubscribeStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.SubscribeState subscribeState); void onStreamsAdded(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsRemoved(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onError(@NonNull BroadcastException exception); void onConnectionStateChanged(@NonNull Stage stage, @NonNull Stage.ConnectionState state, @Nullable BroadcastException exception);
Pour la plupart de ces méthodes, les Stage
et ParticipantInfo
correspondantes sont fournies.
Les informations fournies par le moteur de rendu 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 onParticipantPublishStateChanged
. 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.
Le StageRenderer
peut être attaché à la classe de l’étape :
stage.addRenderer(renderer); // multiple renderers can be added
Notez que seuls les participants à la publication déclenchent onParticipantJoined
et que chaque fois qu’un participant arrête de publier ou quitte la session de l’étape, onParticipantLeft
est déclenché.
Publier un flux multimédia
Les appareils locaux tels que les microphones et les caméras intégrés sont découverts via DeviceDiscovery
. Voici un exemple de sélection de la caméra frontale et du microphone par défaut, puis de leur renvoi en tant que LocalStageStreams
à publier par le SDK :
DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); List<Device> devices = deviceDiscovery.listLocalDevices(); List<LocalStageStream> publishStreams = new ArrayList<LocalStageStream>(); Device frontCamera = null; Device microphone = null; // Create streams using the front camera, first microphone for (Device device : devices) { Device.Descriptor descriptor = device.getDescriptor(); if (!frontCamera && descriptor.type == Device.Descriptor.DeviceType.Camera && descriptor.position = Device.Descriptor.Position.FRONT) { front Camera = device; } if (!microphone && descriptor.type == Device.Descriptor.DeviceType.Microphone) { microphone = device; } } ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera); AudioLocalStageStream microphoneStream = new AudioLocalStageStream(microphoneDevice); publishStreams.add(cameraStream); publishStreams.add(microphoneStream); // Provide the streams in Stage.Strategy @Override @NonNull List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return publishStreams; }
Afficher et supprimer des participants
Une fois l’abonnement effectué, vous recevrez un tableau d’objets StageStream
via la fonction onStreamsAdded
du moteur de rendu. Vous pouvez récupérer l’aperçu à partir d’un ImageStageStream
:
ImagePreviewView preview = ((ImageStageStream)stream).getPreview(); // Add the view to your view hierarchy LinearLayout previewHolder = findViewById(R.id.previewHolder); preview.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); previewHolder.addView(preview);
Vous pouvez récupérer les statistiques au niveau de l’audio à partir d’un AudioStageStream
:
((AudioStageStream)stream).setStatsCallback((peak, rms) -> { // handle statistics });
Lorsqu’un participant arrête de publier ou en cas de désabonnement, la fonction onStreamsRemoved
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 de la hiérarchie des vues.
onStreamsRemoved
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
enAUDIO_ONLY
. -
Le participant distant quitte l’étape.
-
Le participant local quitte l’étape.
Comme onStreamsRemoved
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 streamsToPublishForParticipant
.
Important : si une nouvelle instance d’objet LocalStageStream
est renvoyée par streamsToPublishForParticipant
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
Lorsqu’un participant modifie l’état muet de son flux vidéo ou audio, la fonction onStreamMutedChanged
du moteur de rendu est invoquée avec une liste des flux qui ont changé. Utilisez la méthode getMuted
sur StageStream
pour mettre à jour votre interface utilisateur en conséquence.
@Override void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams) { for (StageStream stream : streams) { boolean muted = stream.getMuted(); // handle UI changes } }
Obtenir les statistiques WebRTC
Pour obtenir les dernières statistiques WebRTC relatives à un flux de publication ou d’abonnement, utilisez requestRTCStats
sur StageStream
. Lorsqu’une collecte est réalisée, vous recevez des statistiques via le StageStream.Listener
que vous pouvez définir sur StageStream
.
stream.requestRTCStats(); @Override void onRTCStats(Map<String, Map<String, String>> statsMap) { for (Map.Entry<String, Map<String, string>> stat : statsMap.entrySet()) { for(Map.Entry<String, String> member : stat.getValue().entrySet()) { Log.i(TAG, stat.getKey() + “ has member “ + member.getKey() + “ with value “ + member.getValue()); } } }
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 ParticipantInfo
:
@Override void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { for (Map.Entry<String, String> entry : participantInfo.userInfo.entrySet()) { Log.i(TAG, “attribute: “ + entry.getKey() + “ = “ + entry.getValue()); } }
Poursuivre la session en arrière-plan
Lorsque l’application passe en arrière-plan, vous souhaiterez peut-être vous abonner uniquement au contenu audio des autres participants distants ou arrêter de le publier. Pour ce faire, mettez à jour votre implémentation Strategy
pour arrêter la publication et abonnez-vous à AUDIO_ONLY
(ou NONE
, le cas échéant).
// Local variables before going into the background boolean shouldPublish = true; Stage.SubscribeType subscribeType = Stage.SubscribeType.AUDIO_VIDEO; // Stage.Strategy implementation @Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return shouldPublish; } @Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return subscribeType; } // In our Activity, modify desired publish/subscribe when we go to background, then call refreshStrategy to update the stage @Override void onStop() { super.onStop(); shouldPublish = false; subscribeTpye = Stage.SubscribeType.AUDIO_ONLY; stage.refreshStrategy(); }
Activer/désactiver le codage en couches avec Simulcast
Lors de la publication d’un flux multimédia, le SDK transmet des flux vidéo de haute et de faible qualité, afin que les participants distants puissent s’abonner au flux même s’ils disposent d’une bande passante descendante limitée. L’encodage en couches avec simulcast est activé par défaut. Vous pouvez le désactiver en utilisant la classe StageVideoConfiguration.Simulcast
:
// Disable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(false); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
Limitations relatives à la configuration vidéo
Le kit SDK ne permet pas de forcer l’utilisation du mode portrait ou paysage à l’aide de StageVideoConfiguration.setSize(BroadcastConfiguration.Vec2 size)
. En orientation portrait, la plus petite dimension est utilisée comme largeur, tandis qu’en orientation paysage, il s’agit de la hauteur. Cela signifie que les deux appels suivants à setSize
ont le même effet sur la configuration vidéo :
StageVideo Configuration config = new StageVideo Configuration(); config.setSize(BroadcastConfiguration.Vec2(720f, 1280f); config.setSize(BroadcastConfiguration.Vec2(1280f, 720f);
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. Deux erreurs principales sont liées à la perte de la connexion réseau :
-
Code d’erreur 1400, message : « PeerConnection perdue en raison d’une erreur réseau inconnue »
-
Code d’erreur 1300, message : « Le nombre de nouvelles tentatives est épuisé »
Si la première erreur est reçue, mais pas la seconde, cela signifie que le kit SDK est toujours connecté à l’étape et qu’il essaiera de rétablir ses connexions automatiquement. Par mesure de sécurité, vous pouvez appeler refreshStrategy
sans modifier les valeurs de retour de la méthode stratégique, afin de déclencher une tentative de reconnexion manuelle.
Si la deuxième erreur est reçue, les tentatives de reconnexion du kit SDK ont échoué et l’appareil local n’est plus connecté à l’étape. Dans ce cas, essayez de rejoindre l’étape en appelant join
une fois votre connexion réseau rétablie.
En général, le fait de rencontrer des erreurs après avoir correctement rejoint une étape indique que le kit SDK n’a pas réussi à rétablir la connexion. Créez un objet Stage
et essayez de le joindre lorsque les conditions du réseau s’améliorent.
Utilisation de microphones Bluetooth
Pour publier à l’aide de microphones Bluetooth, vous devez établir une connexion Bluetooth SCO :
Bluetooth.startBluetoothSco(context); // Now bluetooth microphones can be used … // Must also stop bluetooth SCO Bluetooth.stopBluetoothSco(context);