Gérer la vidéo pendant les appels
Découvrez comment gérer les appels vidéo avec les kits de développement logiciel (SDK) Azure Communication Services. Nous allons apprendre à gérer la réception et l’envoi de vidéo pendant un appel.
Prérequis
- Compte Azure avec un abonnement actif. Créez un compte gratuitement.
- Une ressource Communication Services déployée. Créez une ressource Communication Services.
- Un jeton d’accès utilisateur pour activer le client appelant. Pour plus d’informations, consultez Créer et gérer des jetons d’accès.
- Facultatif : suivez le guide de démarrage rapide permettant d’ajouter l’appel vocal à votre application.
Installer le SDK
Utilisez la commande npm install
pour installer le SDK Azure Communication Services Common et le SDK Azure Communication Services Calling pour JavaScript :
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Initialiser les objets nécessaires
Une instance CallClient
est requise pour la plupart des opérations d’appel. Lorsque vous créez une instance CallClient
, vous pouvez la configurer avec des options personnalisées comme une instance Logger
.
Avec l’instance CallClient
, vous pouvez créer une instance CallAgent
en appelant createCallAgent
. Cette méthode renvoie un objet d’instance CallAgent
de manière asynchrone.
La méthode createCallAgent
utilise CommunicationTokenCredential
comme argument. Elle accepte un jeton d’accès utilisateur.
Vous pouvez utiliser la méthode getDeviceManager
sur l’instance CallClient
pour accéder à deviceManager
.
const { CallClient } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential} = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the logger's log level
setLogLevel('verbose');
// Redirect log output to console, file, buffer, REST API, or whatever location you want
AzureLogger.log = (...args) => {
console.log(...args); // Redirect log output to console
};
const userToken = '<USER_TOKEN>';
callClient = new CallClient(options);
const tokenCredential = new AzureCommunicationTokenCredential(userToken);
const callAgent = await callClient.createCallAgent(tokenCredential, {displayName: 'optional Azure Communication Services user name'});
const deviceManager = await callClient.getDeviceManager()
Comment gérer au mieux la connectivité du SDK à l’infrastructure Microsoft
L’instance Call Agent
vous aide à gérer les appels (pour rejoindre ou démarrer des appels). Pour fonctionner, votre SDK d’appel doit se connecter à l’infrastructure Microsoft pour recevoir des notifications d’appels entrants et coordonner d’autres détails de l’appel. Votre Call Agent
a deux états possibles :
Connecté : un Call Agent
dont la valeur connectionStatue est égale à Connected
signifie que le SDK client est connecté et capable de recevoir des notifications de l’infrastructure Microsoft.
Déconnecté : un Call Agent
dont la valeur connectionStatue est égale à Disconnected
indique qu’un problème empêche le SDK de se connecter correctement. Call Agent
doit être recréé.
invalidToken
: si un jeton a expiré ou n’est pas valide, l’instanceCall Agent
se déconnecte avec cette erreur.connectionIssue
: en cas de problème de connexion du client à l’infrastructure Microsoft, après plusieurs tentatives,Call Agent
lève l’erreurconnectionIssue
.
Vous pouvez vérifier si votre Call Agent
local est connecté à l’infrastructure Microsoft en inspectant la valeur actuelle de la propriété connectionState
. Pendant un appel actif, vous pouvez écouter l’événement connectionStateChanged
pour déterminer si Call Agent
passe de l’état Connecté à l’état Déconnecté.
const connectionState = callAgentInstance.connectionState;
console.log(connectionState); // it may return either of 'Connected' | 'Disconnected'
const connectionStateCallback = (args) => {
console.log(args); // it will return an object with oldState and newState, each of having a value of either of 'Connected' | 'Disconnected'
// it will also return reason, either of 'invalidToken' | 'connectionIssue'
}
callAgentInstance.on('connectionStateChanged', connectionStateCallback);
Gestion des appareils
Pour commencer à utiliser de la vidéo avec le SDK Calling, vous devez être en mesure de gérer les appareils. Les appareils vous permettent de contrôler ce que transmet l’audio et la vidéo à l’appel.
Avec deviceManager
, vous pouvez énumérer les appareils locaux qui peuvent transmettre vos flux audio et vidéo dans un appel. Vous pouvez également utiliser deviceManager
pour demander l’autorisation d’accéder aux micros et appareils photo de l’appareil local.
Vous pouvez accéder à deviceManager
en appelant la méthode callClient.getDeviceManager()
:
const deviceManager = await callClient.getDeviceManager();
Récupérer les appareils locaux
Pour accéder aux appareils locaux, vous pouvez utiliser les méthodes d’énumération deviceManager
suivantes : getCameras()
et getMicrophones
. Ces méthodes sont des actions asynchrones.
// Get a list of available video devices for use.
const localCameras = await deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
// Get a list of available microphone devices for use.
const localMicrophones = await deviceManager.getMicrophones(); // [AudioDeviceInfo, AudioDeviceInfo...]
// Get a list of available speaker devices for use.
const localSpeakers = await deviceManager.getSpeakers(); // [AudioDeviceInfo, AudioDeviceInfo...]
Définir les appareils par défaut
Après avoir identifié les appareils disponibles, vous pouvez définir les appareils par défaut pour le micro, le haut-parleur et l’appareil photo. Si les paramètres par défaut du client ne sont pas définis, le Kit de développement logiciel (SDK) Communication Services utilise les paramètres par défaut du système d’exploitation.
Microphone
Accéder à l’appareil utilisé
// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;
Définir l’appareil à utiliser
// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);
Intervenant
Accéder à l’appareil utilisé
// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Définir l’appareil à utiliser
// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);
Appareil photo
Accéder à l’appareil utilisé
// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Définir l’appareil à utiliser
// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);
Chaque CallAgent
peut choisir son propre microphone et haut-parleurs sur son associé(e)DeviceManager
. Nous recommandons à différents CallAgents
d’utiliser des micros et des haut-parleurs différents. Ils ne doivent pas partager les mêmes micros et haut-parleurs. En cas de partage, les diagnostics accessibles à l’utilisateur du micro peuvent être déclenchés et entraîner l’arrêt du micro en fonction du navigateur/système d’exploitation.
Flux vidéo local
Pour pouvoir envoyer une vidéo dans un appel, vous devez créer un objet LocalVideoStream
.
const localVideoStream = new LocalVideoStream(camera);
L’appareil photo passé en tant que paramètre est l’un des objets VideoDeviceInfo
retournés par la méthode deviceManager.getCameras()
.
Un LocalVideoStream
possède les propriétés suivantes :
source
: les informations des appareils.
const source = localVideoStream.source;
mediaStreamType
: Peut êtreVideo
,ScreenSharing
ouRawMedia
.
const type: MediaStreamType = localVideoStream.mediaStreamType;
Aperçu de la caméra locale
Vous pouvez utiliser deviceManager
et VideoStreamRenderer
pour commencer à afficher le flux de votre caméra locale.
Une fois qu’un LocalVideoStream
est créé, utilisez-le pour configurer VideoStreamRenderer
. Une fois le VideoStreamRenderer
créé, appelez sa méthode createView()
pour obtenir une vue que vous pouvez ajouter en tant qu’enfant à votre page.
Ce flux n’est pas envoyé à d’autres participants ; il s’agit d’un flux d’aperçu local.
// To start viewing local camera preview
const cameras = await deviceManager.getCameras();
const camera = cameras[0];
const localVideoStream = new LocalVideoStream(camera);
const videoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
Arrêter l’aperçu local
Pour arrêter l’appel d’aperçu local, supprimez la vue dérivée de VideoStreamRenderer
.
Une fois le VideoStreamRenderer supprimé, supprimez la vue de l’arborescence HTML en appelant la méthode removeChild()
à partir du nœud DOM contenant votre aperçu.
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
Demander l’autorisation d’accès à une caméra et à un microphone
Une application ne peut pas utiliser l’appareil photo ou le micro sans autorisations. Vous pouvez utiliser le deviceManager pour demander à un utilisateur d’autoriser l’accès à son appareil photo et/ou micro :
const result = await deviceManager.askDevicePermission({audio: true, video: true});
Une fois la promesse résolue, la méthode est retournée avec un objet DeviceAccess
qui indique si les autorisations audio
et video
ont été accordées :
console.log(result.audio);
console.log(result.video);
Notes
- L’événement
videoDevicesUpdated
se déclenche lorsque des appareils vidéo sont en cours de branchement/débranchés. - L’événement
audioDevicesUpdated
se déclenche lorsque des appareils audio sont branchés. - Lorsque le DeviceManager est créé, il n’a d’abord connaissance d’aucun appareil si les autorisations n’ont pas encore été accordées. Aucun nom d’appareil n’est donc initialement indiqué et il ne contient pas d’informations détaillées sur les appareils. Si nous appelons ensuite l’API DeviceManager.askPermission(), l’utilisateur est invité à accéder à l’appareil. Lorsque l’utilisateur sélectionne « allow » pour autoriser l’accès, le gestionnaire d’appareils s’informe sur les appareils présents sur le système, met à jour ses listes d’appareils et émet les événements « audioDevicesUpdated » et « videoDevicesUpdated ». Si un utilisateur actualise la page et crée un gestionnaire d’appareils, ce dernier est en mesure de se renseigner sur les appareils puisque l’utilisateur lui a précédemment accordé l’accès. Ses listes d’appareils sont initialement remplies et il n’émet pas d’événements « audioDevicesUpdated » ou « videoDevicesUpdated ».
- L’énumération/sélection d’intervenants n’est pas prise en charge sur Android Chrome, iOS Safari et macOS Safari.
Passer un appel avec une caméra vidéo
Important
Actuellement, un seul flux vidéo local sortant est pris en charge.
Pour passer un appel vidéo, vous devez énumérer les caméras locales avec la méthode getCameras()
dans deviceManager
.
Après avoir sélectionné une caméra, utilisez-la pour construire une instance LocalVideoStream
.
Transmettez-la dans videoOptions
en tant qu’élément du tableau localVideoStream
à la méthode CallAgent
startCall
.
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
const placeCallOptions = {videoOptions: {localVideoStreams:[localVideoStream]}};
const userCallee = { communicationUserId: '<ACS_USER_ID>' }
const call = callAgent.startCall([userCallee], placeCallOptions);
- Vous pouvez également joindre un appel avec une vidéo avec l’API
CallAgent.join()
, et accepter et appeler avec la vidéo avec l’APICall.Accept()
. - Quand votre appel est connecté, il commence automatiquement à envoyer un flux vidéo à l’autre participant à partir de la caméra sélectionnée.
Démarrer et arrêter l’envoi d’une vidéo locale pendant un appel
Démarrer la vidéo
Pour démarrer une vidéo pendant un appel, vous devez énumérer les caméras à l’aide de la méthode getCameras
sur l’objet deviceManager
.
Créez ensuite une instance de LocalVideoStream
avec la caméra souhaitée, puis passez l’objet LocalVideoStream
dans la méthode startVideo
d’un objet d’appel existant :
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);
Arrêter la vidéo
Après que vous avez commencé à envoyer une vidéo, une instance LocalVideoStream
de type Video
est ajoutée à la collection localVideoStreams
sur une instance d’appel.
Rechercher le flux vidéo dans l’objet Call
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
Arrêter la vidéo locale Pour arrêter la vidéo locale pendant un appel, passez l’instance localVideoStream
utilisée pour la vidéo à la méthode stopVideo de Call
:
await call.stopVideo(localVideoStream);
Vous pouvez basculer vers un autre appareil photo tout en ayant un LocalVideoStream actif en appelant switchSource
sur cette instance LocalVideoStream
:
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
Si l’appareil vidéo spécifié n’est pas disponible :
- Lors d’un appel, si votre vidéo est désactivée et que vous démarrez la vidéo à l’aide de
call.startVideo()
, cette méthode génère unSourceUnavailableError
et le diagnostic face à l’utilisateur(-trice)cameraStartFailed
est défini sur true. - Un appel à la méthode
localVideoStream.switchSource()
entraîne la modification decameraStartFailed
en True. Notre guide Diagnostics des appels fournit des informations supplémentaires sur la façon de diagnostiquer les problèmes liés aux appels.
Pour vérifier si la vidéo locale est activée ou désactivée, vous pouvez utiliser la méthode Call
isLocalVideoStarted
, qui retourne true ou false :
// Check if local video is on or off
call.isLocalVideoStarted;
Pour écouter les modifications apportées à la vidéo locale, vous pouvez vous abonner et vous désabonner à l’événement isLocalVideoStartedChanged :
// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
// Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
// Callback();
});
Démarrer et arrêter le partage d’écran pendant un appel
Pour démarrer le partage d’écran pendant un appel, vous pouvez utiliser la méthode asynchrone startScreenSharing()
sur un objet Call
:
Démarrer le partage d’écran
// Start screen sharing
await call.startScreenSharing();
Remarque : L’envoi de partages d’écran est pris en charge uniquement par un navigateur de bureau.
Rechercher le partage d’écran dans la collection de LocalVideoStream
Après avoir lancé avec succès l’envoi du partage d’écran, une instance LocalVideoStream
de type ScreenSharing
, est ajoutée à la collection localVideoStreams
de l’instance d’appel.
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );
Arrêter le partage d’écran
Pour arrêter le partage d’écran pendant un appel, vous pouvez utiliser l’API asynchrone stopScreenSharing :
// Stop screen sharing
await call.stopScreenSharing();
Vérifier l’état du partage d’écran
Pour vérifier si le partage d’écran est activé ou désactivé, vous pouvez utiliser l’API isScreenSharingOn qui retourne true ou false :
// Check if screen sharing is on or off
call.isScreenSharingOn;
Pour écouter les modifications apportées au partage d’écran, vous pouvez vous abonner et vous désabonner à l’événement isScreenSharingOnChanged :
// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
// Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
// Callback();
});
Important
Cette fonctionnalité d’Azure Communication Services est actuellement en préversion.
Ces interfaces de programmation d’applications et kits de développement logiciel (SDK) en préversion sont fournis sans contrat au niveau du service. Nous vous recommandons de ne pas les utiliser pour les charges de travail de production. Certaines fonctionnalités peuvent être limitées ou non prises en charge.
Pour plus d’informations, consultez Conditions d’utilisation supplémentaires relatives aux préversions de Microsoft Azure.
La préversion de partage d’écran local est en préversion publique et disponible dans le cadre de la version 1.15.1-beta.1+.
Préversion du partage d’écran local
Vous pouvez utiliser un VideoStreamRenderer
pour commencer le rendu de flux à partir de votre partage d’écran local afin de voir ce que vous envoyez comme flux de partage d’écran.
// To start viewing local screen share preview
await call.startScreenSharing();
const localScreenSharingStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing' });
const videoStreamRenderer = new VideoStreamRenderer(localScreenSharingStream);
const view = await videoStreamRenderer.createView();
htmlElement.appendChild(view.target);
// To stop viewing local screen share preview.
await call.stopScreenSharing();
view.dispose();
htmlElement.removeChild(view.target);
// Screen sharing can also be stoped by clicking on the native browser's "Stop sharing" button.
// The isScreenSharingOnChanged event will be triggered where you can check the value of call.isScreenSharingOn.
// If the value is false, then that means screen sharing is turned off and so we can go ahead and dispose the screen share preview.
// This event is also triggered for the case when stopping screen sharing via Call.stopScreenSharing() API.
call.on('isScreenSharingOnChanged', () => {
if (!call.isScreenSharingOn) {
view.dispose();
htmlElement.removeChild(view.target);
}
});
Afficher les flux de partage de partage d’écran ou de vidéo de participants distants
Pour afficher une vidéo ou un partage d’écran d’un participant distant, la première chose à faire consiste à obtenir une référence sur le RemoteVideoStream que vous souhaitez afficher.
Pour cela, vous pouvez parcourir le tableau ou le flux vidéo (videoStreams
) du RemoteParticipant
. La collection de participants distants est accessible via l’objet Call
.
const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;
Pour afficher RemoteVideoStream
, vous devez vous abonner à son événement isAvailableChanged
. Si la propriété isAvailable
passe à true
, un participant distant envoie un flux vidéo.
Une fois que cela se produit, créez une instance de VideoStreamRenderer
, puis une instance VideoStreamRendererView
en utilisant la méthode createView
asynchrone.
Vous pouvez ensuite attacher view.target
à un élément d’interface utilisateur quelconque.
Chaque fois que la disponibilité d’un flux distant change, vous pouvez détruire la totalité de VideoStreamRenderer
ou un VideoStreamRendererView
spécifique.
Si vous décidez de les conserver, la vue affiche un cadre vidéo vide.
// Reference to the html's div where we would display a grid of all remote video stream from all participants.
let remoteVideosGallery = document.getElementById('remoteVideosGallery');
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
let renderer = new VideoStreamRenderer(remoteVideoStream);
let view;
let remoteVideoContainer = document.createElement('div');
remoteVideoContainer.className = 'remote-video-container';
let loadingSpinner = document.createElement('div');
// See the css example below for styling the loading spinner.
loadingSpinner.className = 'loading-spinner';
remoteVideoStream.on('isReceivingChanged', () => {
try {
if (remoteVideoStream.isAvailable) {
const isReceiving = remoteVideoStream.isReceiving;
const isLoadingSpinnerActive = remoteVideoContainer.contains(loadingSpinner);
if (!isReceiving && !isLoadingSpinnerActive) {
remoteVideoContainer.appendChild(loadingSpinner);
} else if (isReceiving && isLoadingSpinnerActive) {
remoteVideoContainer.removeChild(loadingSpinner);
}
}
} catch (e) {
console.error(e);
}
});
const createView = async () => {
// Create a renderer view for the remote video stream.
view = await renderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.appendChild(view.target);
remoteVideosGallery.appendChild(remoteVideoContainer);
}
// Remote participant has switched video on/off
remoteVideoStream.on('isAvailableChanged', async () => {
try {
if (remoteVideoStream.isAvailable) {
await createView();
} else {
view.dispose();
remoteVideosGallery.removeChild(remoteVideoContainer);
}
} catch (e) {
console.error(e);
}
});
// Remote participant has video on initially.
if (remoteVideoStream.isAvailable) {
try {
await createView();
} catch (e) {
console.error(e);
}
}
console.log(`Initial stream size: height: ${remoteVideoStream.size.height}, width: ${remoteVideoStream.size.width}`);
remoteVideoStream.on('sizeChanged', () => {
console.log(`Remote video stream size changed: new height: ${remoteVideoStream.size.height}, new width: ${remoteVideoStream.size.width}`);
});
}
CSS pour styliser la boucle de chargement sur le flux vidéo distant.
.remote-video-container {
position: relative;
}
.loading-spinner {
border: 12px solid #f3f3f3;
border-radius: 50%;
border-top: 12px solid #ca5010;
width: 100px;
height: 100px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
position: absolute;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
Vidéo de qualité distante
Le kit SDK WebJS Azure Communication Services fournit, à compter de la version 1.15.1, une fonctionnalité appelée OVC (Optimal Video Count).
Vous pouvez utiliser cette fonction pour informer les applications, au moment de l’exécution, du nombre de vidéos entrantes provenant de différents participants qui peuvent être affichées de manière optimale à un moment donné lors d’un appel de groupe (2 participants ou plus).
Cette fonction expose une propriété optimalVideoCount
qui change dynamiquement au cours de l’appel en fonction du réseau et des capacités matérielles d’un point d’extrémité local. La valeur de optimalVideoCount
précise le nombre de vidéos provenant de différents participants que l’application doit afficher à un moment donné. Les applications doivent gérer ces changements et mettre à jour le nombre de vidéos affichées en fonction de la recommandation. Une période anti-rebond (« debounce ») d’environ 10 s existe entre chaque mise à jour.
Utilisation La fonctionnalité optimalVideoCount
est une fonctionnalité d’appel. Vous devez référencer la fonctionnalité OptimalVideoCount
via la méthode feature
de l’objet Call
. Vous pouvez ensuite définir un écouteur par le biais de la méthode on
de OptimalVideoCountCallFeature
pour être averti quand optimalVideoCount change. Pour vous désabonner des changements, vous pouvez appeler la méthode off
. Le nombre maximal de vidéos entrantes pouvant être restituées est de 16. Pour prendre en charge 16 vidéos entrantes, l’ordinateur devrait disposer d’au moins 16 Go de RAM et d’un processeur à 4 cœurs ou plus qui n’a pas plus de 3 ans.
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
Exemple d’utilisation : l’application doit s’abonner aux changements d’Optimal Video Count dans des appels de groupe. Vous pouvez gérer un changement d’Optimal Video Count en créant un renderer (méthode createView
) ou en supprimant des vues (dispose
) et en mettant à jour la disposition de l’application en conséquence.
Propriétés du flux vidéo distant
Les flux vidéo distants ont les propriétés suivantes :
const id: number = remoteVideoStream.id;
id
: ID d’un flux vidéo distant.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
mediaStreamType
: Peut êtreVideo
ouScreenSharing
.
const isAvailable: boolean = remoteVideoStream.isAvailable;
isAvailable
: Définir si le point de terminaison du ou de la participant(e) distant(e) envoie activement un flux.
const isReceiving: boolean = remoteVideoStream.isReceiving;
isReceiving
:Informe l’application si des données de flux vidéo distant sont reçues ou non.
L’indicateur passe à
false
dans les scénarios suivants :- Un participant distant utilisant un navigateur mobile fait passer l’application du navigateur à l’arrière-plan.
- Un participant distant ou l’utilisateur recevant la vidéo fait face à un problème réseau qui affecte considérablement la qualité de la vidéo.
- Un participant distant utilisant Safari sur macOS/iOS sélectionne « Pause » (Suspendre) dans sa barre d’adresses.
- Un participant distant est déconnecté du réseau.
- Un participant distant sur un appareil mobile tue ou termine le navigateur.
- Un participant distant sur un appareil mobile ou un ordinateur de bureau verrouille son appareil. Ce scénario s’applique également si le participant distant se trouve sur un ordinateur de bureau qui se met en veille.
L’indicateur passe à
true
dans les scénarios suivants :- Un participant distant utilisant un navigateur mobile fait repasser son navigateur au premier plan.
- Un participant distant utilisant Safari sur macOS/iOS sélectionne « Resume » (Reprendre) dans sa barre d’adresses après avoir interrompu sa vidéo.
- Un participant distant se reconnecte au réseau après une déconnexion temporaire.
- Un participant distant sur un appareil mobile déverrouille son appareil et retourne à l’appel sur son navigateur mobile.
Cette fonctionnalité améliore l’expérience utilisateur pour le rendu de flux vidéo distants.
Vous pouvez afficher une boucle de chargement sur le flux vidéo distant lorsque l’indicateur de récupération change en « false ». Vous n’êtes pas obligé d’implémenter une boucle de progression du chargement, mais il est de coutume d’en inclure une pour offrir une meilleure expérience utilisateur.
const size: StreamSize = remoteVideoStream.size;
size
: taille du flux avec des informations sur la largeur et la hauteur de la vidéo.
Méthodes et propriétés de VideoStreamRenderer
await videoStreamRenderer.createView();
Créez une instance de VideoStreamRendererView
qui peut être jointe dans l’interface utilisateur de l’application pour afficher le flux vidéo distant. Utilisez la méthode createView()
asynchrone ; elle est résolue quand le flux est prêt à être affiché et retourne un objet avec une propriété target
qui représente l’élément video
pouvant être inséré n’importe où dans l’arborescence DOM.
videoStreamRenderer.dispose();
Supprimez videoStreamRenderer
et toutes les instances de VideoStreamRendererView
associées.
Méthodes et propriétés de VideoStreamRendererView
Quand vous créez un VideoStreamRendererView
, vous pouvez spécifier les propriétés scalingMode
et isMirrored
. scalingMode
Peut être Stretch
, Crop
ou Fit
. Si isMirrored
est spécifié, le flux affiché est retourné verticalement.
const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });
Chaque instance VideoStreamRendererView
a une propriété target
qui représente la surface du rendu. Attachez cette propriété dans l’interface utilisateur de l’application :
htmlElement.appendChild(view.target);
Vous pouvez mettre à jour scalingMode
en appelant la méthode updateScalingMode
:
view.updateScalingMode('Crop');
Envoyez des flux vidéo provenant de deux caméras différentes, lors d’un même appel, à partir du même appareil de bureau.
Important
Cette fonctionnalité d’Azure Communication Services est actuellement en préversion.
Ces interfaces de programmation d’applications et kits de développement logiciel (SDK) en préversion sont fournis sans contrat au niveau du service. Nous vous recommandons de ne pas les utiliser pour les charges de travail de production. Certaines fonctionnalités peuvent être limitées ou non prises en charge.
Pour plus d’informations, consultez Conditions d’utilisation supplémentaires relatives aux préversions de Microsoft Azure.
L’envoi de flux vidéo à partir de deux appareils photo différents dans le même appel est pris en charge dans le cadre de la version 1.17.1-beta.1+ sur les navigateurs de bureau pris en charge.
- Vous pouvez envoyer des flux vidéo provenant de deux caméras différentes à partir d’un seul onglet/application de navigateur de bureau, dans le même appel, avec l’extrait de code suivant :
// Create your first CallAgent with identity A
const callClient1 = new CallClient();
const callAgent1 = await callClient1.createCallAgent(tokenCredentialA);
const deviceManager1 = await callClient1.getDeviceManager();
// Create your second CallAgent with identity B
const callClient2 = new CallClient();
const callAgent2 = await callClient2.createCallAgent(tokenCredentialB);
const deviceManager2 = await callClient2.getDeviceManager();
// Join the call with your first CallAgent
const camera1 = await deviceManager1.getCameras()[0];
const callObj1 = callAgent1.join({ groupId: ‘123’}, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera1)] } });
// Join the same call with your second CallAgent and make it use a different camera
const camera2 = (await deviceManager2.getCameras()).filter((camera) => { return camera !== camera1 })[0];
const callObj2 = callAgent2.join({ groupId: '123' }, { videoOptions: { localVideoStreams: [new LocalVideoStream(camera2)] } });
//Mute the microphone and speakers of your second CallAgent’s Call, so that there is no echos/noises.
await callObj2.muteIncomingAudio();
await callObj2.mute();
Limites :
- Pour cela, vous devez utiliser deux instances différentes de
CallAgent
avec des identités différentes. L’extrait de code montre deux agents d’appel utilisés, chacun avec son propre objet Call. - Dans l’exemple de code, les deux CallAgents rejoignent le même appel (même ID d’appel). Vous pouvez également joindre différents appels avec chaque agent et envoyer une vidéo sur un appel et une autre vidéo sur l’autre appel.
- L’envoi de la même caméra dans les deux CallAgent n’est pas possible. Ils doivent être deux caméras différentes.
- L’envoi de deux caméras différentes avec un CallAgent n’est actuellement pas pris en charge.
- Sur macOS Safari, les effets vidéo flous d’arrière-plan (de @azure/communication-effects), ne peuvent être appliqués qu’à une seule caméra, et pas les deux en même temps.
Installer le SDK
Recherchez votre fichier build.gradle
au niveau du projet et ajoutez mavenCentral()
à la liste des référentiels sous buildscript
et allprojects
:
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Ensuite, dans votre fichier build.gradle
au niveau du module, ajoutez les lignes suivantes à la section dependencies
:
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
Initialiser les objets nécessaires
Pour créer une instance CallAgent
, vous devez appeler la méthode createCallAgent
sur une instance CallClient
. Cet appel retourne de façon asynchrone un objet d’instance CallAgent
.
La méthode createCallAgent
prend CommunicationUserCredential
en tant qu’argument, qui encapsule un jeton d’accès.
Pour accéder à DeviceManager
, vous devez d’abord créer une instance callAgent
. Vous pouvez ensuite utiliser la méthode CallClient.getDeviceManager
pour obtenir DeviceManager
.
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential).get();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Pour définir un nom d’affichage pour l’appelant, utilisez cette autre méthode :
String userToken = '<user token>';
CallClient callClient = new CallClient();
CommunicationTokenCredential tokenCredential = new CommunicationTokenCredential(userToken);
android.content.Context appContext = this.getApplicationContext(); // From within an activity, for instance
CallAgentOptions callAgentOptions = new CallAgentOptions();
callAgentOptions.setDisplayName("Alice Bob");
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
CallAgent callAgent = callClient.createCallAgent(appContext, tokenCredential, callAgentOptions).get();
Gestion des appareils
Pour commencer à utiliser de la vidéo avec les appels, vous devez savoir comment gérer les appareils. Les appareils vous permettent de contrôler ce que transmet l’audio et la vidéo à l’appel.
DeviceManager
vous permet d’énumérer les appareils locaux utilisables dans un appel pour transmettre vos flux audio/vidéo. Il vous permet également de demander à un utilisateur l’autorisation d’accéder à son microphone et à sa caméra à l’aide de l’API de navigateur natif.
Vous pouvez accéder au deviceManager
en appelant la méthode callClient.getDeviceManager()
.
Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Énumérer les appareils locaux
Pour accéder aux appareils locaux, vous pouvez utiliser les méthodes d’énumération sur le Gestionnaire d’appareils. L’énumération est une action synchrone.
// Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
Aperçu de la caméra locale
Vous pouvez utiliser DeviceManager
et Renderer
pour commencer à afficher le flux de votre caméra locale. Ce flux n’est pas envoyé à d’autres participants ; il s’agit d’un flux d’aperçu local. Cette action est asynchrone.
VideoDeviceInfo videoDevice = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(videoDevice, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
RenderingOptions renderingOptions = new RenderingOptions(ScalingMode.Fit);
VideoStreamRenderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
VideoStreamRendererView uiView = previewRenderer.createView(renderingOptions);
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
Passer un appel 1:1 avec une caméra vidéo
Avertissement
Actuellement, un seul flux vidéo local sortant est pris en charge. Pour passer un appel avec vidéo, vous devez énumérer les caméras locales à l’aide de l’API deviceManager
getCameras
.
Une fois la caméra sélectionnée, utilisez-la pour créer une instance LocalVideoStream
et la transmettre à videoOptions
en tant qu’élément dans le tableau localVideoStream
vers une méthode call
.
Une fois l’appel connecté, il commence automatiquement à envoyer un flux vidéo aux autres participants à partir de la caméra sélectionnée.
Remarque
Pour des raisons de confidentialité, la vidéo ne sera pas partagée lors de l’appel si elle n’est pas visionnée localement. Pour plus d’informations, consultez Aperçu de la caméra locale.
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentVideoStream = new LocalVideoStream(desiredCamera, appContext);
LocalVideoStream[] localVideoStreams = new LocalVideoStream[1];
localVideoStreams[0] = currentVideoStream;
VideoOptions videoOptions = new VideoOptions(localVideoStreams);
// Render a local preview of video so the user knows that their video is being shared
Renderer previewRenderer = new VideoStreamRenderer(currentVideoStream, appContext);
View uiView = previewRenderer.createView(new CreateViewOptions(ScalingMode.FIT));
// Attach the uiView to a viewable location on the app at this point
layout.addView(uiView);
CommunicationUserIdentifier[] participants = new CommunicationUserIdentifier[]{ new CommunicationUserIdentifier("<acs user id>") };
StartCallOptions startCallOptions = new StartCallOptions();
startCallOptions.setVideoOptions(videoOptions);
Call call = callAgent.startCall(context, participants, startCallOptions);
Démarrer et arrêter l’envoi d’une vidéo locale
Pour démarrer une vidéo, vous devez énumérer les caméras à l’aide de l’API getCameraList
sur l’objet deviceManager
. Cela crée une nouvelle instance de LocalVideoStream
transmettant la caméra souhaitée, et en la transmettant dans l’API startVideo
comme argument :
VideoDeviceInfo desiredCamera = <get-video-device>; // See the `Enumerate local devices` topic above
Context appContext = this.getApplicationContext();
LocalVideoStream currentLocalVideoStream = new LocalVideoStream(desiredCamera, appContext);
VideoOptions videoOptions = new VideoOptions(currentLocalVideoStream);
Future startVideoFuture = call.startVideo(appContext, currentLocalVideoStream);
startVideoFuture.get();
Une fois que vous avez commencé à envoyer une vidéo, une instance LocalVideoStream
est ajoutée à la collection localVideoStreams
sur l’instance d’appel.
List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).
Pour arrêter la vidéo locale, transmettez l’instance LocalVideoStream
disponible dans la collection localVideoStreams
:
call.stopVideo(appContext, currentLocalVideoStream).get();
Vous pouvez basculer vers une autre caméra pendant l’envoi d’une vidéo en appelant switchSource
sur une instance LocalVideoStream
:
currentLocalVideoStream.switchSource(source).get();
Afficher les flux vidéo des participants distants
Pour répertorier les flux vidéo et les flux de partage d’écran des participants distants, inspectez les collections videoStreams
:
List<RemoteParticipant> remoteParticipants = call.getRemoteParticipants();
RemoteParticipant remoteParticipant = remoteParticipants.get(0); // Please make sure there are remote participants in the list before calling get(0).
List<RemoteVideoStream> remoteStreams = remoteParticipant.getVideoStreams();
RemoteVideoStream remoteParticipantStream = remoteStreams.get(0); // Please make sure there are video streams in the list before calling get(0).
MediaStreamType streamType = remoteParticipantStream.getType(); // of type MediaStreamType.Video or MediaStreamType.ScreenSharing
Pour afficher le RemoteVideoStream
d’un participant distant, vous devez vous abonner à un événement OnVideoStreamsUpdated
.
Dans l’événement, la modification de la propriété isAvailable
sur true indique que le participant distant envoie actuellement un flux. Une fois cette opération effectuée, créez une nouvelle instance d’un(e) Renderer
, puis créez un nouveau/une nouvelle RendererView
à l’aide de l’API createView
asynchrone et joignez view.target
n’importe où dans l’interface utilisateur de votre application.
Chaque fois que la disponibilité d’un flux distant change, vous pouvez choisir de détruire le convertisseur tout entier ou un RendererView
spécifique, ou de les conserver, mais cela entraîne l’affichage d’une image vidéo vide.
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteParticipantStream, appContext);
VideoStreamRendererView uiView = remoteVideoRenderer.createView(new RenderingOptions(ScalingMode.FIT));
layout.addView(uiView);
remoteParticipant.addOnVideoStreamsUpdatedListener(e -> onRemoteParticipantVideoStreamsUpdated(p, e));
void onRemoteParticipantVideoStreamsUpdated(RemoteParticipant participant, RemoteVideoStreamsEvent args) {
for(RemoteVideoStream stream : args.getAddedRemoteVideoStreams()) {
if(stream.getIsAvailable()) {
startRenderingVideo();
} else {
renderer.dispose();
}
}
}
Propriétés du flux vidéo distant
Un flux vidéo distant possède plusieurs propriétés
Id
- ID d’un flux vidéo distant
int id = remoteVideoStream.getId();
MediaStreamType
– peut être « Video » ou « ScreenSharing »
MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
– indique si le point de terminaison du participant distant envoie activement un flux
boolean availability = remoteVideoStream.isAvailable();
Méthodes et propriétés de convertisseur
Objet du convertisseur suivant les API
- Créez une instance
VideoStreamRendererView
qui peut être jointe ultérieurement dans l’interface utilisateur de l’application pour afficher le flux vidéo distant.
// Create a view for a video stream
VideoStreamRendererView.createView()
- Supprimez le convertisseur et tous les
VideoStreamRendererView
associés à ce convertisseur. À appeler lorsque vous avez supprimé toutes les vues associées de l’interface utilisateur.
VideoStreamRenderer.dispose()
StreamSize
- taille (largeur/hauteur) d’un flux vidéo distant
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();
Méthodes et propriétés de RendererView
Lors de la création d’un VideoStreamRendererView
, vous pouvez spécifier les propriétés ScalingMode
et mirrored
qui s’appliquent à cette vue : le mode de mise à l’échelle peut être « CROP » ou « FIT ».
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));
Le RendererView créé peut alors être attaché à l’interface utilisateur de l’application à l’aide de l’extrait de code suivant :
layout.addView(rendererView);
Vous pouvez modifier le mode de mise à l’échelle ultérieurement en appelant l’API updateScalingMode
sur l’objet RendererView avec l’un des arguments suivants : « ScalingMode.CROP » ou « ScalingMode.FIT ».
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
Configurer votre système
Effectuez les étapes suivantes pour configurer votre système.
Créer le projet Xcode
Dans Xcode, créez un projet iOS et sélectionnez le modèle Single View App. Cet article utilise l’infrastructure SwiftUI. Vous devez donc définir le langage sur Swift et l’interface sur SwiftUI.
Vous n’allez pas créer de tests dans cet article. N’hésitez pas à désactiver la case Inclure des tests.
Installer le package et les dépendances à l’aide de CocoaPods
Créez un Podfile pour votre application, comme cet exemple :
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
Exécutez
pod install
.Ouvrez
.xcworkspace
en utilisant Xcode.
Demander l’accès au microphone
Pour accéder au microphone de l’appareil, vous devez mettre à jour la liste des propriétés d’informations de votre application à l’aide de NSMicrophoneUsageDescription
. Affectez la valeur associée à une chaîne qui sera incluse dans la boîte de dialogue affichée par le système pour demander l’accès à l’utilisateur.
Cliquez avec le bouton droit sur l’entrée Info.plist de l’arborescence du projet, puis sélectionnez Ouvrir en tant que>Code source. Ajoutez les lignes suivantes à la section <dict>
tout en haut, puis enregistrez le fichier.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Configurer le framework d’application
Ouvrez le fichier ContentView.swift
de votre projet. Ajoutez une déclaration import
en haut du fichier pour importer la bibliothèque AzureCommunicationCalling
. En outre, importez AVFoundation
. Vous en avez besoin pour les demandes d’autorisations audio dans le code.
import AzureCommunicationCalling
import AVFoundation
Initialiser CallAgent
Pour créer une instance de CallAgent
à partir de CallClient
, vous devez utiliser une méthode callClient.createCallAgent
qui retourne de manière asynchrone un objet CallAgent
après qu’il a été initialisé.
Pour créer un client d’appel, transmettez un objet CommunicationTokenCredential
:
import AzureCommunication
let tokenString = "token_string"
var userCredential: CommunicationTokenCredential?
do {
let options = CommunicationTokenRefreshOptions(initialToken: token, refreshProactively: true, tokenRefresher: self.fetchTokenSync)
userCredential = try CommunicationTokenCredential(withOptions: options)
} catch {
updates("Couldn't created Credential object", false)
initializationDispatchGroup!.leave()
return
}
// tokenProvider needs to be implemented by Contoso, which fetches a new token
public func fetchTokenSync(then onCompletion: TokenRefreshOnCompletion) {
let newToken = self.tokenProvider!.fetchNewToken()
onCompletion(newToken, nil)
}
Transmettez l’objet CommunicationTokenCredential
que vous avez créé à CallClient
et définissez le nom complet :
self.callClient = CallClient()
let callAgentOptions = CallAgentOptions()
options.displayName = " iOS Azure Communication Services User"
self.callClient!.createCallAgent(userCredential: userCredential!,
options: callAgentOptions) { (callAgent, error) in
if error == nil {
print("Create agent succeeded")
self.callAgent = callAgent
} else {
print("Create agent failed")
}
})
Gérer des unités
Pour commencer à utiliser de la vidéo avec les appels, vous devez savoir comment gérer les appareils. Les appareils vous permettent de contrôler ce que transmet l’audio et la vidéo à l’appel.
DeviceManager
permet d’énumérer les appareils locaux qui peuvent être utilisés dans un appel pour transmettre des flux audio ou vidéo. Il vous permet également de demander l’autorisation à un utilisateur d’accéder à un microphone ou à une caméra. Vous pouvez accéder à deviceManager
sur l’objet callClient
.
self.callClient!.getDeviceManager { (deviceManager, error) in
if (error == nil) {
print("Got device manager instance")
self.deviceManager = deviceManager
} else {
print("Failed to get device manager instance")
}
}
Énumérer les appareils locaux
Pour accéder aux appareils locaux, vous pouvez utiliser les méthodes d’énumération sur le gestionnaire d’appareils. L’énumération est une action synchrone.
// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]
Obtenir l’aperçu de la caméra locale
Vous pouvez utiliser Renderer
pour commencer à afficher un flux à partir de votre caméra locale. Ce flux n’est pas envoyé à d’autres participants ; il s’agit d’un flux d’aperçu local. Cette action est asynchrone.
let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()
Obtenir les propriétés de l’aperçu de la caméra locale
Le renderer a un ensemble de propriétés et de méthodes qui vous permettent de contrôler le rendu.
// Constructor can take in LocalVideoStream or RemoteVideoStream
let localRenderer = VideoStreamRenderer(localVideoStream:localVideoStream)
let remoteRenderer = VideoStreamRenderer(remoteVideoStream:remoteVideoStream)
// [StreamSize] size of the rendering view
localRenderer.size
// [VideoStreamRendererDelegate] an object you provide to receive events from this Renderer instance
localRenderer.delegate
// [Synchronous] create view
try! localRenderer.createView()
// [Synchronous] create view with rendering options
try! localRenderer!.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.fit))
// [Synchronous] dispose rendering view
localRenderer.dispose()
Passer un appel 1:1 avec vidéo
Pour obtenir une instance du gestionnaire d’appareils, consultez la section relative à la gestion des appareils.
let firstCamera = self.deviceManager!.cameras.first
self.localVideoStreams = [LocalVideoStream]()
self.localVideoStreams!.append(LocalVideoStream(camera: firstCamera!))
let videoOptions = VideoOptions(localVideoStreams: self.localVideoStreams!)
let startCallOptions = StartCallOptions()
startCallOptions.videoOptions = videoOptions
let callee = CommunicationUserIdentifier('UserId')
self.callAgent?.startCall(participants: [callee], options: startCallOptions) { (call, error) in
if error == nil {
print("Successfully started outgoing video call")
self.call = call
} else {
print("Failed to start outgoing video call")
}
}
Afficher les flux vidéo des participants distants
Les participants distants peuvent lancer un partage vidéo ou d’écran pendant un appel.
Gérer les flux de partage vidéo ou de partage d’écran de participants distants
Pour lister les flux de participants distants, examinez les collections videoStreams
.
var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]
Obtenir les propriétés du flux vidéo distant
var type: MediaStreamType = remoteParticipantVideoStream.type // 'MediaStreamTypeVideo'
var isAvailable: Bool = remoteParticipantVideoStream.isAvailable // indicates if remote stream is available
var id: Int = remoteParticipantVideoStream.id // id of remoteParticipantStream
Afficher les flux des participants distants
Pour commencer à afficher les flux des participants distants, utilisez le code suivant.
let renderer = VideoStreamRenderer(remoteVideoStream: remoteParticipantVideoStream)
let targetRemoteParticipantView = renderer?.createView(withOptions: CreateViewOptions(scalingMode: ScalingMode.crop))
// To update the scaling mode later
targetRemoteParticipantView.update(scalingMode: ScalingMode.fit)
Obtenir les méthodes et propriétés du renderer vidéo à distance
// [Synchronous] dispose() - dispose renderer and all `RendererView` associated with this renderer. To be called when you have removed all associated views from the UI.
remoteVideoRenderer.dispose()
Configurer votre système
Effectuez les étapes suivantes pour configurer votre système.
Créer le projet Visual Studio
Pour une application de plateforme Windows universelle, dans Visual Studio 2022, créez un projet Application vide (Windows universel). Après avoir entré le nom du projet, n’hésitez pas à choisir un kit de développement logiciel (SDK) Windows d’une version ultérieure à 10.0.17763.0.
Pour une application WinUI 3, créez un projet avec le modèle Application vide, Empaquetée (WinUI 3 dans Desktop) pour configurer une application WinUI 3 monopage. Le SDK d’application Windows version 1.3 ou ultérieure est nécessaire.
Installer le package et les dépendances à l’aide du Gestionnaire de package NuGet
Les API et les bibliothèques du Kit de développement logiciel (SDK) Appel sont accessibles au public via un package NuGet.
Pour rechercher, télécharger et installer le package NuGet du SDK Appel :
- Ouvrez le Gestionnaire de package NuGet en sélectionnant Outils>Gestionnaire de package NuGet>Gérer les packages NuGet pour la solution.
- Sélectionnez Parcourir, puis entrez Azure.Communication.Calling.WindowsClient dans la zone de recherche.
- Vérifiez que la case Inclure la préversion est cochée.
- Sélectionnez le package Azure.Communication.Calling.WindowsClient, puis Azure.Communication.Calling.WindowsClient 1.4.0-beta.1 ou une version plus récente.
- Cochez la case qui correspond au projet Azure Communication Services dans le volet de droite.
- Sélectionnez Installer.
Demander l’accès au microphone
L’application nécessite un accès à la caméra pour s’exécuter correctement. Dans les applications UWP, la capacité de la caméra doit être déclarée dans le fichier manifeste de l’application.
Les étapes suivantes illustrent comment y parvenir.
- Dans le panneau
Solution Explorer
, double-cliquez sur le fichier avec l’extension.appxmanifest
. - Cliquez sur l’onglet
Capabilities
. - Cochez la case
Camera
dans la liste des capacités.
Créer des boutons d’interface utilisateur pour passer et raccrocher l’appel
Cet exemple d’application simple contient deux boutons. Un pour passer l’appel et une autre pour raccrocher un appel passé. Les étapes suivantes illustrent comment ajouter ces boutons à l’application.
- Dans le panneau
Solution Explorer
, double-cliquez sur le fichier nomméMainPage.xaml
pour UWP, ouMainWindows.xaml
pour WinUI 3. - Dans le panneau central, recherchez le code XAML sous l’aperçu de l’interface utilisateur.
- Modifiez le code XAML en le remplaçant par l’extrait suivant :
<TextBox x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" />
<StackPanel>
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" />
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" />
</StackPanel>
Configuration de l’application avec les API du Kit de développement logiciel (SDK) Appel
Les API du Kit de développement logiciel (SDK) Appel se trouvent dans deux espaces de noms différents. Les étapes suivantes informent le compilateur C# de ces espaces de noms, ce qui permet à IntelliSense de Visual Studio de faciliter le développement du code.
- Dans le panneau
Solution Explorer
, cliquez sur la flèche située sur le côté gauche du fichier nomméMainPage.xaml
pour UWP, ouMainWindows.xaml
pour WinUI 3. - Double-cliquez sur le fichier nommé
MainPage.xaml.cs
ouMainWindows.xaml.cs
. - Ajoutez les commandes suivantes au bas des instructions
using
actuelles.
using Azure.Communication.Calling.WindowsClient;
Conservez MainPage.xaml.cs
ou MainWindows.xaml.cs
ouvert. Les étapes suivantes y ajouteront du code.
Autoriser les interactions avec l’application
Les boutons d’interface utilisateur précédemment ajoutés doivent fonctionner en même temps qu’un CommunicationCall
passé. Cela signifie qu’un membre de données CommunicationCall
doit être ajouté à la classe MainPage
ou MainWindow
.
En outre, pour permettre à l’opération asynchrone qui crée CallAgent
de réussir, un membre de données CallAgent
doit également être ajouté à la même classe.
Ajoutez les membres de données suivants à la classe MainPage
ou MainWindow
:
CallAgent callAgent;
CommunicationCall call;
Créer des gestionnaires de bouton
Précédemment, deux boutons d’interface utilisateur ont été ajoutés au code XAML. Le code suivant ajoute les gestionnaires à exécuter lorsqu’un utilisateur sélectionne le bouton. Le code suivant doit être ajouté après les membres de données de la section précédente.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
Modèle objet
Les classes et les interfaces suivantes gèrent certaines des principales fonctionnalités de la bibliothèque de client Appel Azure Communication Services pour UWP.
Nom | Description |
---|---|
CallClient |
CallClient est le point d’entrée principal de la bibliothèque d’appels de client. |
CallAgent |
CallAgent sert à lancer et à joindre des appels. |
CommunicationCall |
CommunicationCall sert à gérer les appels passés ou rejoints. |
CommunicationTokenCredential |
CommunicationTokenCredential sert de jeton d’informations d'identification pour initier le CallAgent . |
CallAgentOptions |
Le CallAgentOptions contient des informations pour identifier l’appelant. |
HangupOptions |
Le HangupOptions informe si un appel doit être arrêté à tous ses participants. |
Enregistrer le gestionnaire de schéma vidéo
Un composant d’interface utilisateur, comme MediaElement ou MediaPlayerElement de XAML, vous avez besoin que l’application enregistre une configuration pour le rendu des flux vidéo locaux et distants.
Ajoutez le contenu suivant entre les balises Package
du Package.appxmanifest
:
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Initialiser CallAgent
Pour créer une instance CallAgent
à partir de CallClient
, vous devez utiliser la méthode CallClient.CreateCallAgentAsync
qui retourne de façon asynchrone un objet CallAgent
une fois qu’il est initialisé.
Pour créer CallAgent
, vous devez transmettre un objet CallTokenCredential
et un objet CallAgentOptions
. Gardez à l’esprit que CallTokenCredential
lève une exception si un jeton malformé est transmis.
Vous devez ajouter le code suivant dans une fonction d’assistance à appeler au cours de l’initialisation de l’application.
var callClient = new CallClient();
this.deviceManager = await callClient.GetDeviceManagerAsync();
var tokenCredential = new CallTokenCredential("<AUTHENTICATION_TOKEN>");
var callAgentOptions = new CallAgentOptions()
{
DisplayName = "<DISPLAY_NAME>"
};
this.callAgent = await callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.CallsUpdated += Agent_OnCallsUpdatedAsync;
this.callAgent.IncomingCallReceived += Agent_OnIncomingCallAsync;
Modifiez le <AUTHENTICATION_TOKEN>
avec un jeton d’informations d’identification valide pour votre ressource. Référez-vous à la documentation relative aux jetons d’accès utilisateur si un jeton d’informations d’identification doit être sourcé.
Passer un appel 1:1 avec une caméra vidéo
Les objets nécessaires à la création d’un objet CallAgent
sont maintenant prêts. Il est temps de créer CallAgent
de manière asynchrone et de passer un appel vidéo.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
if (callString.StartsWith("8:")) // 1:1 Azure Communication Services call
{
this.call = await StartAcsCallAsync(callString);
}
}
if (this.call != null)
{
this.call.RemoteParticipantsUpdated += OnRemoteParticipantsUpdatedAsync;
this.call.StateChanged += OnStateChangedAsync;
}
}
private async Task<CommunicationCall> StartAcsCallAsync(string acsCallee)
{
var options = await GetStartCallOptionsAsynnc();
var call = await this.callAgent.StartCallAsync( new [] { new UserCallIdentifier(acsCallee) }, options);
return call;
}
var micStream = new LocalOutgoingAudioStream(); // Create a default local audio stream
var cameraStream = new LocalOutgoingVideoStreamde(this.viceManager.Cameras.FirstOrDefault() as VideoDeviceDetails); // Create a default video stream
private async Task<StartCallOptions> GetStartCallOptionsAsynnc()
{
return new StartCallOptions() {
OutgoingAudioOptions = new OutgoingAudioOptions() { IsMuted = true, Stream = micStream },
OutgoingVideoOptions = new OutgoingVideoOptions() { Streams = new OutgoingVideoStream[] { cameraStream } }
};
}
Aperçu de la caméra locale
Nous pouvons éventuellement configurer l’aperçu de la caméra locale. La vidéo peut être affichée par le biais du composant MediaPlayerElement
:
<Grid>
<MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>
Pour initialiser le composant MediaPlayerElement
de l’aperçu local :
private async void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cameraStream != null)
{
await cameraStream?.StopPreviewAsync();
if (this.call != null)
{
await this.call?.StopVideoAsync(cameraStream);
}
}
var selectedCamerea = CameraList.SelectedItem as VideoDeviceDetails;
cameraStream = new LocalOutgoingVideoStream(selectedCamerea);
var localUri = await cameraStream.StartPreviewAsync();
LocalVideo.Source = MediaSource.CreateFromUri(localUri);
if (this.call != null) {
await this.call?.StartVideoAsync(cameraStream);
}
}
Afficher le flux de la caméra à distance
Configurez le gestionnaire d’événements en réponse à l’événement OnCallsUpdated
:
private async void OnCallsUpdatedAsync(object sender, CallsUpdatedEventArgs args)
{
var removedParticipants = new List<RemoteParticipant>();
var addedParticipants = new List<RemoteParticipant>();
foreach(var call in args.RemovedCalls)
{
removedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
}
foreach (var call in args.AddedCalls)
{
addedParticipants.AddRange(call.RemoteParticipants.ToList<RemoteParticipant>());
}
await OnParticipantChangedAsync(removedParticipants, addedParticipants);
}
private async void OnRemoteParticipantsUpdatedAsync(object sender, ParticipantsUpdatedEventArgs args)
{
await OnParticipantChangedAsync(
args.RemovedParticipants.ToList<RemoteParticipant>(),
args.AddedParticipants.ToList<RemoteParticipant>());
}
private async Task OnParticipantChangedAsync(IEnumerable<RemoteParticipant> removedParticipants, IEnumerable<RemoteParticipant> addedParticipants)
{
foreach (var participant in removedParticipants)
{
foreach(var incomingVideoStream in participant.IncomingVideoStreams)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
if (remoteVideoStream != null)
{
await remoteVideoStream.StopPreviewAsync();
}
}
participant.VideoStreamStateChanged -= OnVideoStreamStateChanged;
}
foreach (var participant in addedParticipants)
{
participant.VideoStreamStateChanged += OnVideoStreamStateChanged;
}
}
private void OnVideoStreamStateChanged(object sender, VideoStreamStateChangedEventArgs e)
{
CallVideoStream callVideoStream = e.CallVideoStream;
switch (callVideoStream.StreamDirection)
{
case StreamDirection.Outgoing:
OnOutgoingVideoStreamStateChanged(callVideoStream as OutgoingVideoStream);
break;
case StreamDirection.Incoming:
OnIncomingVideoStreamStateChanged(callVideoStream as IncomingVideoStream);
break;
}
}
Démarrez l’affichage du flux vidéo distant sur MediaPlayerElement
:
private async void OnIncomingVideoStreamStateChanged(IncomingVideoStream incomingVideoStream)
{
switch (incomingVideoStream.State)
{
case VideoStreamState.Available:
{
switch (incomingVideoStream.Kind)
{
case VideoStreamKind.RemoteIncoming:
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
var uri = await remoteVideoStream.StartPreviewAsync();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
});
/* Or WinUI 3
this.DispatcherQueue.TryEnqueue(() => {
RemoteVideo.Source = MediaSource.CreateFromUri(uri);
RemoteVideo.MediaPlayer.Play();
});
*/
break;
case VideoStreamKind.RawIncoming:
break;
}
break;
}
case VideoStreamState.Started:
break;
case VideoStreamState.Stopping:
break;
case VideoStreamState.Stopped:
if (incomingVideoStream.Kind == VideoStreamKind.RemoteIncoming)
{
var remoteVideoStream = incomingVideoStream as RemoteIncomingVideoStream;
await remoteVideoStream.StopPreviewAsync();
}
break;
case VideoStreamState.NotAvailable:
break;
}
}
Terminer un appel
Une fois qu’un appel est passé, la méthode HangupAsync
de l’objet CommunicationCall
doit être utilisée pour raccrocher l’appel.
Une instance HangupOptions
doit également être utilisée pour indiquer à tous les participants si l’appel doit être interrompu.
Le code suivant doit être ajouté dans HangupButton_Click
.
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
foreach (var localVideoStream in call.OutgoingVideoStreams)
{
await call.StopVideoAsync(localVideoStream);
}
try
{
if (cameraStream != null)
{
await cameraStream.StopPreviewAsync();
}
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
catch(Exception ex)
{
var errorCode = unchecked((int)(0x0000FFFFU & ex.HResult));
if (errorCode != 98) // Sample error code, sam_status_failed_to_hangup_for_everyone (98)
{
throw;
}
}
}
}
Exécuter le code
Vérifiez que Visual Studio génère l’application pour x64
, x86
ou ARM64
, puis appuyez sur F5
pour commencer à exécuter l’application. Après cela, cliquez sur le bouton CommunicationCall
pour passer un appel à l’appelé défini.
N’oubliez pas que la première fois que l’application s’exécute, le système invite l’utilisateur à accorder l’accès au microphone.