Gestire video durante le chiamate
Informazioni su come gestire le videochiamate con Servizi di comunicazione di Azure SDKS. Si apprenderà come gestire la ricezione e l'invio di video all'interno di una chiamata.
Prerequisiti
- Un account Azure con una sottoscrizione attiva. Creare un account gratuitamente.
- Una risorsa di Servizi di comunicazione distribuita. Creare una risorsa di Servizi di comunicazione.
- Token di accesso utente per abilitare il client chiamante. Per altre informazioni, vedere Creare e gestire i token di accesso.
- Facoltativo: Completare la guida introduttiva per aggiungere chiamate vocali all'applicazione
Installazione dell'SDK
Usare il npm install
comando per installare l'SDK comune e chiamante Servizi di comunicazione di Azure per JavaScript:
npm install @azure/communication-common --save
npm install @azure/communication-calling --save
Inizializzare gli oggetti necessari
Per la maggior parte delle operazioni di chiamata è necessaria un'istanza CallClient
di . Quando si crea una nuova CallClient
istanza, è possibile configurarla con opzioni personalizzate come un'istanza Logger
di .
Con l'istanza di è possibile creare un'istanza CallClient
CallAgent
chiamando .createCallAgent
Questo metodo restituisce in modo asincrono un CallAgent
oggetto istanza.
Il createCallAgent
metodo usa CommunicationTokenCredential
come argomento. Accetta un token di accesso utente.
È possibile usare il metodo nell'istanza getDeviceManager
CallClient
di per accedere deviceManager
a .
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()
Come gestire al meglio la connettività dell'SDK all'infrastruttura Microsoft
L'istanza Call Agent
consente di gestire le chiamate (per partecipare o avviare le chiamate). Per lavorare con l'SDK per chiamate, è necessario connettersi all'infrastruttura Microsoft per ricevere notifiche delle chiamate in arrivo e coordinare altri dettagli delle chiamate. Gli Call Agent
stati possibili sono due:
Connessione ed: un Call Agent
valore connectionStatue indica Connected
che l'SDK client è connesso e in grado di ricevere notifiche dall'infrastruttura Microsoft.
Disconnesso : valore Call Agent
connectionStatue di Disconnected
stati che impedisce all'SDK di connettersi correttamente. Call Agent
deve essere ricreato.
invalidToken
: se un token è scaduto o l'istanza non è validaCall Agent
si disconnette con questo errore.connectionIssue
: se si verifica un problema con il client che si connette a Microsoft infrascture, dopo molti tentativiCall Agent
espone l'erroreconnectionIssue
.
È possibile verificare se l'infrastruttura locale Call Agent
è connessa all'infrastruttura Microsoft controllando il valore corrente della connectionState
proprietà. Durante una chiamata attiva è possibile restare in ascolto dell'evento connectionStateChanged
per determinare se Call Agent
cambia da Connessione stato disconnesso a Disconnesso.
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);
Gestione dei dispositivi
Per iniziare a usare il video con Calling SDK, è necessario essere in grado di gestire i dispositivi. I dispositivi consentono di controllare cosa trasmette audio e video alla chiamata.
deviceManager
Con , è possibile enumerare i dispositivi locali in grado di trasmettere i flussi audio e video in una chiamata. È anche possibile usare per richiedere l'autorizzazione deviceManager
per accedere ai microfoni e alle fotocamere del dispositivo locale.
È possibile accedere deviceManager
chiamando il callClient.getDeviceManager()
metodo :
const deviceManager = await callClient.getDeviceManager();
Ottenere i dispositivi locali
Per accedere ai dispositivi locali, è possibile usare i deviceManager
metodi getCameras()
di enumerazione e getMicrophones
. Questi metodi sono azioni asincrone.
// 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...]
Impostare i dispositivi predefiniti
Dopo aver appreso quali dispositivi sono disponibili per l'uso, è possibile impostare i dispositivi predefiniti per microfono, altoparlante e fotocamera. Se le impostazioni predefinite del client non sono impostate, Communication Services SDK usa le impostazioni predefinite del sistema operativo.
Microphone
Accedere al dispositivo usato
// Get the microphone device that is being used.
const defaultMicrophone = deviceManager.selectedMicrophone;
Impostazione del dispositivo da usare
// Set the microphone device to use.
await deviceManager.selectMicrophone(localMicrophones[0]);
Relatore
Accedere al dispositivo usato
// Get the speaker device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Impostazione del dispositivo da usare
// Set the speaker device to use.
await deviceManager.selectSpeaker(localSpeakers[0]);
Fotocamera
Accedere al dispositivo usato
// Get the camera device that is being used.
const defaultSpeaker = deviceManager.selectedSpeaker;
Impostazione del dispositivo da usare
// Set the speaker device to use.
await deviceManager.selectSpeaker(localCameras[0]);
Ognuno CallAgent
può scegliere il proprio microfono e altoparlanti sul proprio associato DeviceManager
. È consigliabile usare microfoni e altoparlanti diversi CallAgents
. Non dovrebbero condividere gli stessi microfoni né altoparlanti. Se si verifica la condivisione, è possibile che venga attivata la diagnostica utente microfono e il microfono smette di funzionare a seconda del browser o del sistema operativo.
Flusso video locale
Per poter inviare video in una chiamata, è necessario creare un LocalVideoStream
oggetto .
const localVideoStream = new LocalVideoStream(camera);
La fotocamera passata come parametro è uno degli VideoDeviceInfo
oggetti restituiti dal deviceManager.getCameras()
metodo .
Un LocalVideoStream
oggetto ha le proprietà seguenti:
source
: informazioni sul dispositivo.
const source = localVideoStream.source;
mediaStreamType
: può essereVideo
,ScreenSharing
oRawMedia
.
const type: MediaStreamType = localVideoStream.mediaStreamType;
Anteprima della fotocamera locale
È possibile usare deviceManager
e VideoStreamRenderer
per avviare il rendering dei flussi dalla fotocamera locale.
Dopo aver creato un oggetto LocalVideoStream
, usarlo per configurarloVideoStreamRenderer
. Una volta creato, VideoStreamRenderer
chiamare il createView()
relativo metodo per ottenere una visualizzazione che è possibile aggiungere come figlio alla pagina.
Questo flusso non viene inviato ad altri partecipanti; è un feed di anteprima locale.
// 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);
Arrestare l'anteprima locale
Per arrestare la chiamata di anteprima locale, eliminare nella vista derivata da VideoStreamRenderer
.
Dopo aver eliminato VideoStreamRenderer, rimuovere la visualizzazione dall'albero html chiamando il metodo dal nodo DOM contenente l'anteprima removeChild()
.
// To stop viewing local camera preview
view.dispose();
htmlElement.removeChild(view.target);
Richiedere l'autorizzazione per fotocamera e microfono
Un'applicazione non può usare la fotocamera o il microfono senza autorizzazioni. È possibile usare deviceManager per richiedere a un utente di concedere autorizzazioni per fotocamera e/o microfono:
const result = await deviceManager.askDevicePermission({audio: true, video: true});
Una volta risolta la promessa, il metodo restituisce con un DeviceAccess
oggetto che indica se audio
sono state concesse le autorizzazioni e video
:
console.log(result.audio);
console.log(result.video);
Note
videoDevicesUpdated
l'evento viene generato quando i dispositivi video sono collegati/scollegati.audioDevicesUpdated
l'evento viene generato quando i dispositivi audio sono collegati.- Quando viene creato DeviceManager, inizialmente non conosce alcun dispositivo se le autorizzazioni non sono ancora concesse, quindi inizialmente il nome del dispositivo è vuoto e non contiene informazioni dettagliate sul dispositivo. Se si chiama quindi l'API DeviceManager.askPermission(), all'utente viene richiesto l'accesso al dispositivo. Quando l'utente seleziona "consenti" per concedere l'accesso a Gestione dispositivi apprende informazioni sui dispositivi nel sistema, aggiorna gli elenchi di dispositivi e genera gli eventi "audioDevicesUpdated" e "videoDevicesUpdated". Se un utente aggiorna la pagina e crea un gestore di dispositivi, gestione dispositivi è in grado di ottenere informazioni sui dispositivi perché l'utente ha concesso l'accesso in precedenza. Ha gli elenchi di dispositivi compilati inizialmente e non genera eventi 'audioDevicesUpdated' né 'videoDevicesUpdated'.
- L'enumerazione/selezione del parlante non è supportata in Android Chrome, iOS Safari o macOS Safari.
Effettuare una chiamata con videocamera
Importante
Attualmente è supportato un solo flusso video locale in uscita.
Per effettuare una videochiamata, è necessario enumerare le fotocamere locali usando il getCameras()
metodo in deviceManager
.
Dopo aver selezionato una fotocamera, usarla per costruire un'istanza LocalVideoStream
.
Passarlo all'interno come elemento all'interno videoOptions
della localVideoStream
matrice al CallAgent
startCall
metodo .
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);
- È anche possibile partecipare a una chiamata con video con
CallAgent.join()
l'API e accettare e chiamare con video con l'APICall.Accept()
. - Quando la chiamata si connette, avvia automaticamente l'invio di un flusso video dalla fotocamera selezionata all'altro partecipante.
Avviare e interrompere l'invio di video locali durante una chiamata
Avvia video
Per avviare un video durante una chiamata, è necessario enumerare le fotocamere usando il getCameras
metodo sull'oggetto deviceManager
.
Creare quindi una nuova istanza di LocalVideoStream
con la fotocamera desiderata e quindi passare l'oggetto LocalVideoStream
al startVideo
metodo di un oggetto chiamata esistente:
const deviceManager = await callClient.getDeviceManager();
const cameras = await deviceManager.getCameras();
const camera = cameras[0]
const localVideoStream = new LocalVideoStream(camera);
await call.startVideo(localVideoStream);
Interrompi video
Dopo aver avviato correttamente l'invio di video, viene aggiunta un'istanza LocalVideoStream
di tipo Video
alla raccolta in un'istanza localVideoStreams
di chiamata.
Trovare il flusso video nell'oggetto Call
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'Video'} );
Arrestare il video locale Per arrestare il video locale durante una chiamata, passare l'istanza localVideoStream
usata per il video al metodo stopVideo di Call
:
await call.stopVideo(localVideoStream);
È possibile passare a un dispositivo fotocamera diverso mentre si dispone di un LocalVideoStream attivo richiamando switchSource
su tale LocalVideoStream
istanza:
const cameras = await callClient.getDeviceManager().getCameras();
const camera = cameras[1];
localVideoStream.switchSource(camera);
Se il dispositivo video specificato non è disponibile:
- Durante una chiamata, se il video è disattivato e si avvia il video usando
call.startVideo()
, questo metodo genera unaSourceUnavailableError
diagnostica ecameraStartFailed
l'utente è impostato su true. - Una chiamata al metodo determina
cameraStartFailed
l'impostazionelocalVideoStream.switchSource()
su true. La guida alla diagnostica delle chiamate fornisce informazioni aggiuntive su come diagnosticare i problemi correlati alle chiamate.
Per verificare se il video locale è attivato o disattivato, è possibile usare il Call
metodo isLocalVideoStarted
, che restituisce true o false:
// Check if local video is on or off
call.isLocalVideoStarted;
Per ascoltare le modifiche apportate al video locale, è possibile sottoscrivere e annullare la sottoscrizione all'evento isLocalVideoStartedChanged:
// Subscribe to local video event
call.on('isLocalVideoStartedChanged', () => {
// Callback();
});
// Unsubscribe from local video event
call.off('isLocalVideoStartedChanged', () => {
// Callback();
});
Avviare e arrestare la condivisione dello schermo durante una chiamata
Per avviare la condivisione dello schermo durante una chiamata, è possibile usare il metodo startScreenSharing()
asincrono in un Call
oggetto :
Avviare la condivisione dello schermo
// Start screen sharing
await call.startScreenSharing();
Nota: l'invio di screenshare è supportato solo nel browser desktop.
Trovare la condivisione dello schermo nella raccolta di LocalVideoStream
Dopo aver avviato correttamente l'invio della condivisione dello schermo, viene aggiunta un'istanza LocalVideoStream
di tipo ScreenSharing
, alla raccolta nell'istanza localVideoStreams
di chiamata.
const localVideoStream = call.localVideoStreams.find( (stream) => { return stream.mediaStreamType === 'ScreenSharing'} );
Interrompi condivisione dello schermo
Per interrompere la condivisione dello schermo durante una chiamata, è possibile usare stoptScreenSharing dell'API asincrona:
// Stop screen sharing
await call.stopScreenSharing();
Controllare lo stato di condivisione dello schermo
Per verificare se la condivisione dello schermo è attivata o disattivata, è possibile usare l'API isScreenSharingOn, che restituisce true o false:
// Check if screen sharing is on or off
call.isScreenSharingOn;
Per ascoltare le modifiche apportate alla condivisione dello schermo, è possibile sottoscrivere e annullare la sottoscrizione all'evento isScreenSharingOnChanged:
// Subscribe to screen share event
call.on('isScreenSharingOnChanged', () => {
// Callback();
});
// Unsubscribe from screen share event
call.off('isScreenSharingOnChanged', () => {
// Callback();
});
Importante
Questa funzionalità di Servizi di comunicazione di Azure è attualmente in anteprima.
Le API di anteprima e gli SDK vengono forniti senza un contratto di servizio. È consigliabile non usarli per i carichi di lavoro di produzione. Alcune funzionalità potrebbero non essere supportate o potrebbero avere funzionalità limitate.
Per altre informazioni, vedere Condizioni per l'utilizzo supplementari per le anteprime di Microsoft Azure.
L'anteprima della condivisione della schermata locale è disponibile in anteprima pubblica e disponibile come parte della versione 1.15.1-beta.1+.
Anteprima della condivisione dello schermo locale
È possibile usare un VideoStreamRenderer
oggetto per avviare il rendering dei flussi dalla condivisione dello schermo locale in modo da visualizzare ciò che si invia come flusso di condivisione dello schermo.
// 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);
}
});
Eseguire il rendering dei flussi video/screenharing dei partecipanti remoti
Per eseguire il rendering di un video o una condivisione dello schermo partecipante remoto, il primo passaggio consiste nel ottenere un riferimento in RemoteVideoStream di cui si vuole eseguire il rendering.
A tale scopo, è possibile passare attraverso la matrice o il flusso video (videoStreams
) di RemoteParticipant
. L'insieme di partecipanti remoti è accessibile tramite l'oggetto Call
.
const remoteVideoStream = call.remoteParticipants[0].videoStreams[0];
const streamType = remoteVideoStream.mediaStreamType;
Per eseguire il rendering RemoteVideoStream
di , è necessario sottoscrivere l'evento isAvailableChanged
. Se la isAvailable
proprietà viene modificata in true
, un partecipante remoto invia un flusso video.
Successivamente, creare una nuova istanza di VideoStreamRenderer
e quindi creare una nuova VideoStreamRendererView
istanza usando il metodo asincrono createView
.
È quindi possibile collegarsi view.target
a qualsiasi elemento dell'interfaccia utente.
Ogni volta che cambia la disponibilità di un flusso remoto, è possibile eliminare l'intero VideoStreamRenderer
oggetto o un oggetto specifico VideoStreamRendererView
.
Se si decide di mantenerli, la visualizzazione visualizza un fotogramma video vuoto.
// 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 per applicare uno stile allo spinner di caricamento sul flusso video remoto.
.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); }
}
Qualità video remota
Il Servizi di comunicazione di Azure WebJS SDK offre una funzionalità denominata OVC (Optimal Video Count), a partire dalla versione 1.15.1.
Questa funzionalità può essere usata per informare le applicazioni in fase di esecuzione sul numero di video in ingresso provenienti da partecipanti diversi per il rendering ottimale in un determinato momento in una chiamata di gruppo (2+ partecipanti).
Questa funzionalità espone una proprietà optimalVideoCount
che cambia in modo dinamico durante la chiamata in base alle funzionalità di rete e hardware di un endpoint locale. Valore dei dettagli sul numero di optimalVideoCount
video provenienti da un'applicazione partecipante diversa da eseguire in un determinato momento. Le applicazioni devono gestire queste modifiche e aggiornare il numero di video di cui è stato eseguito il rendering di conseguenza alla raccomandazione. Tra ogni aggiornamento è presente un periodo di debounce (circa 10 s).
Usage La optimalVideoCount
funzionalità è una funzionalità di chiamata. È necessario fare riferimento alla funzionalità OptimalVideoCount
tramite il feature
metodo dell'oggetto Call
. È quindi possibile impostare un listener tramite il on
metodo di OptimalVideoCountCallFeature
per ricevere una notifica quando cambia il valore ottimaleVideoCount. Per annullare la sottoscrizione alle modifiche, è possibile chiamare il off
metodo .
const optimalVideoCountFeature = call.feature(Features.OptimalVideoCount);
optimalVideoCountFeature.on('optimalVideoCountChanged', () => {
const localOptimalVideoCountVariable = optimalVideoCountFeature.optimalVideoCount;
})
Esempio di utilizzo: l'applicazione deve sottoscrivere le modifiche del numero ottimale di video nelle chiamate di gruppo. Una modifica del numero ottimale di video può essere gestita creando un nuovo renderer (createView
metodo) o eliminando le visualizzazioni (dispose
) e aggiornando di conseguenza il layout dell'applicazione.
Proprietà del flusso video remoto
I flussi video remoti hanno le proprietà seguenti:
const id: number = remoteVideoStream.id;
id
: ID di un flusso video remoto.
const type: MediaStreamType = remoteVideoStream.mediaStreamType;
mediaStreamType
: può essereVideo
oScreenSharing
.
const isAvailable: boolean = remoteVideoStream.isAvailable;
isAvailable
: definisce se un endpoint partecipante remoto invia attivamente un flusso.
const isReceiving: boolean = remoteVideoStream.isReceiving;
isReceiving
:Informa l'applicazione se i dati del flusso video remoto sono stati ricevuti o meno.
Il flag passa a
false
negli scenari seguenti:- Un partecipante remoto che si trova nel browser per dispositivi mobili porta l'app browser in background.
- Un partecipante remoto o l'utente che riceve il video presenta problemi di rete che influiscono drasticamente sulla qualità video.
- Un partecipante remoto su macOS/iOS Safari seleziona "Sospendi" dalla barra degli indirizzi.
- Un partecipante remoto ha una disconnessione di rete.
- Un partecipante remoto sul dispositivo mobile termina o termina il browser.
- Un partecipante remoto su dispositivi mobili o desktop blocca il dispositivo. Questo scenario si applica anche se il partecipante remoto si trova in un computer desktop e passa alla sospensione.
Il flag passa a
true
negli scenari seguenti:- Un partecipante remoto che si trova nel browser per dispositivi mobili e ha il browser in background lo riporta in primo piano.
- Un partecipante remoto su MacOS/iOS Safari seleziona "Riprendi" dalla barra degli indirizzi dopo aver sospeso il video.
- Un partecipante remoto si riconnette alla rete dopo una disconnessione temporanea.
- Un partecipante remoto sul dispositivo sblocca il dispositivo e torna alla chiamata nel browser per dispositivi mobili.
Questa funzionalità migliora l'esperienza utente per il rendering di flussi video remoti.
È possibile visualizzare uno spinner di caricamento sul flusso video remoto quando il flag isReceived viene modificato in false. Non è necessario implementare il caricatore di caricamento, ma uno spinner di caricamento è l'uso più comune per un'esperienza utente migliore.
const size: StreamSize = remoteVideoStream.size;
size
: dimensioni del flusso con informazioni sulla larghezza e l'altezza del video.
Proprietà e metodi VideoStreamRenderer
await videoStreamRenderer.createView();
Creare un'istanza VideoStreamRendererView
che può essere collegata nell'interfaccia utente dell'applicazione per eseguire il rendering del flusso video remoto, usare il metodo asincrono createView()
, viene risolto quando il flusso è pronto per il rendering e restituisce un oggetto con target
proprietà che rappresenta video
l'elemento che può essere inserito ovunque nell'albero DOM.
videoStreamRenderer.dispose();
videoStreamRenderer
Eliminare e tutte le istanze associateVideoStreamRendererView
.
Metodi e proprietà VideoStreamRendererView
Quando si crea un oggetto VideoStreamRendererView
, è possibile specificare le scalingMode
proprietà e isMirrored
. scalingMode
può essere Stretch
, Crop
o Fit
. Se isMirrored
viene specificato, il flusso sottoposto a rendering viene capovolto verticalmente.
const videoStreamRendererView: VideoStreamRendererView = await videoStreamRenderer.createView({ scalingMode, isMirrored });
Ogni VideoStreamRendererView
istanza ha una target
proprietà che rappresenta la superficie di rendering. Allegare questa proprietà nell'interfaccia utente dell'applicazione:
htmlElement.appendChild(view.target);
È possibile eseguire l'aggiornamento scalingMode
richiamando il updateScalingMode
metodo :
view.updateScalingMode('Crop');
Inviare flussi video da due fotocamere diverse, nella stessa chiamata dallo stesso dispositivo desktop.
Importante
Questa funzionalità di Servizi di comunicazione di Azure è attualmente in anteprima.
Le API di anteprima e gli SDK vengono forniti senza un contratto di servizio. È consigliabile non usarli per i carichi di lavoro di produzione. Alcune funzionalità potrebbero non essere supportate o potrebbero avere funzionalità limitate.
Per altre informazioni, vedere Condizioni per l'utilizzo supplementari per le anteprime di Microsoft Azure.
L'invio di flussi video da due fotocamere diverse nella stessa chiamata è supportato come parte della versione 1.17.1-beta.1+ nei browser supportati dal desktop.
- È possibile inviare flussi video da due fotocamere diverse da una singola scheda/app del browser desktop, nella stessa chiamata, con il frammento di codice seguente:
// 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();
Limitazioni :
- Questa operazione deve essere eseguita con due istanze diverse
CallAgent
usando identità diverse. Il frammento di codice mostra due agenti di chiamata usati, ognuno con il proprio oggetto Call. - Nell'esempio di codice, entrambi i CallAgent si uniscono alla stessa chiamata (stessi ID chiamata). È anche possibile partecipare a chiamate diverse con ogni agente e inviare un video su una chiamata e un video diverso sull'altra chiamata.
- L'invio della stessa fotocamera sia in CallAgent non è supportato. Devono essere due fotocamere diverse.
- L'invio di due fotocamere diverse con un CallAgent non è attualmente supportato.
- In macOS Safari, gli effetti video di sfocatura dello sfondo (da @azure/communication-effects), possono essere applicati solo a una fotocamera e non entrambi contemporaneamente.
Installazione dell'SDK
Individuare il file build.gradle a livello di progetto e aggiungere mavenCentral()
all'elenco dei repository in buildscript
e allprojects
:
buildscript {
repositories {
...
mavenCentral()
...
}
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
Quindi, nel file build.gradle a livello di modulo aggiungere le righe seguenti alla dependencies
sezione :
dependencies {
...
implementation 'com.azure.android:azure-communication-calling:1.0.0'
...
}
Inizializzare gli oggetti necessari
Per creare un'istanza CallAgent
di , è necessario chiamare il metodo in un'istanza createCallAgent
CallClient
di . Questa chiamata restituisce in modo asincrono un CallAgent
oggetto istanza.
Il createCallAgent
metodo accetta CommunicationUserCredential
come argomento, che incapsula un token di accesso.
Per accedere a DeviceManager
, è prima necessario creare un'istanza callAgent
di . È quindi possibile usare il CallClient.getDeviceManager
metodo per ottenere 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();
Per impostare un nome visualizzato per il chiamante, usare questo metodo alternativo:
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();
Gestione dei dispositivi
Per iniziare a usare video con le chiamate, è necessario sapere come gestire i dispositivi. I dispositivi consentono di controllare cosa trasmette audio e video alla chiamata.
DeviceManager
consente di enumerare i dispositivi locali che possono essere usati in una chiamata per trasmettere i flussi audio/video. Consente anche di richiedere l'autorizzazione da un utente per accedere al microfono e alla fotocamera usando l'API del browser nativo.
È possibile accedere deviceManager
chiamando callClient.getDeviceManager()
il metodo .
Context appContext = this.getApplicationContext();
DeviceManager deviceManager = callClient.getDeviceManager(appContext).get();
Enumerare i dispositivi locali
Per accedere ai dispositivi locali, è possibile usare i metodi di enumerazione nella Gestione dispositivi. L'enumerazione è un'azione sincrona.
// Get a list of available video devices for use.
List<VideoDeviceInfo> localCameras = deviceManager.getCameras(); // [VideoDeviceInfo, VideoDeviceInfo...]
Anteprima della fotocamera locale
È possibile usare DeviceManager
e Renderer
per avviare il rendering dei flussi dalla fotocamera locale. Questo flusso non verrà inviato ad altri partecipanti; è un feed di anteprima locale. Si tratta di un'azione asincrona.
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);
Effettuare una chiamata 1:1 con la videocamera
Avviso
Attualmente è supportato un solo flusso video locale in uscita Per effettuare una chiamata con video è necessario enumerare le fotocamere locali usando l'API deviceManager
getCameras
.
Dopo aver selezionato una fotocamera desiderata, usarla per costruire un'istanza LocalVideoStream
e passarla videoOptions
come elemento nella localVideoStream
matrice a un call
metodo.
Una volta che la chiamata si connette, inizierà automaticamente a inviare un flusso video dalla fotocamera selezionata ad altri partecipanti.
Nota
A causa di problemi di privacy, il video non verrà condiviso alla chiamata se non viene visualizzato in anteprima in locale. Per altri dettagli, vedi Anteprima della fotocamera 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);
Avviare e interrompere l'invio di video locali
Per avviare un video, è necessario enumerare le fotocamere usando l'API sull'oggetto getCameraList
deviceManager
. Creare quindi una nuova istanza di LocalVideoStream
passare la fotocamera desiderata e passarla nell'API startVideo
come argomento:
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();
Dopo aver avviato correttamente l'invio di video, LocalVideoStream
un'istanza verrà aggiunta alla raccolta nell'istanza localVideoStreams
di chiamata.
List<LocalVideoStream> videoStreams = call.getLocalVideoStreams();
LocalVideoStream currentLocalVideoStream = videoStreams.get(0); // Please make sure there are VideoStreams in the list before calling get(0).
Per arrestare il video locale, passare l'istanza LocalVideoStream
disponibile nella localVideoStreams
raccolta:
call.stopVideo(appContext, currentLocalVideoStream).get();
È possibile passare a un dispositivo fotocamera diverso mentre il video viene inviato richiamando switchSource
un'istanza LocalVideoStream
di :
currentLocalVideoStream.switchSource(source).get();
Eseguire il rendering dei flussi video dei partecipanti remoti
Per elencare i flussi video e i flussi di condivisione dello schermo dei partecipanti remoti, esaminare le videoStreams
raccolte:
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
Per eseguire il rendering di un RemoteVideoStream
oggetto da un partecipante remoto, è necessario sottoscrivere un OnVideoStreamsUpdated
evento.
All'interno dell'evento, la modifica della isAvailable
proprietà su true indica che il partecipante remoto sta inviando un flusso. In questo caso, creare una nuova istanza di , Renderer
quindi creare un nuovo RendererView
usando l'API asincrona createView
e collegarsi view.target
ovunque nell'interfaccia utente dell'applicazione.
Ogni volta che la disponibilità di un flusso remoto cambia, è possibile scegliere di distruggere l'intero renderer, un elemento specifico RendererView
o mantenerlo, ma ciò comporterà la visualizzazione di fotogrammi video vuoti.
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();
}
}
}
Proprietà del flusso video remoto
Il flusso video remoto ha due proprietà
Id
- ID di un flusso video remoto
int id = remoteVideoStream.getId();
MediaStreamType
- Può essere "Video" o "ScreenSharing"
MediaStreamType type = remoteVideoStream.getMediaStreamType();
isAvailable
- Indica se l'endpoint del partecipante remoto sta inviando attivamente il flusso
boolean availability = remoteVideoStream.isAvailable();
Metodi e proprietà del renderer
Oggetto renderer che segue le API
- Creare un'istanza
VideoStreamRendererView
che può essere collegata in un secondo momento nell'interfaccia utente dell'applicazione per eseguire il rendering del flusso video remoto.
// Create a view for a video stream
VideoStreamRendererView.createView()
- Eliminare il renderer e tutti associati
VideoStreamRendererView
a questo renderer. Per essere chiamato quando sono state rimosse tutte le visualizzazioni associate dall'interfaccia utente.
VideoStreamRenderer.dispose()
StreamSize
- dimensioni (larghezza/altezza) di un flusso video remoto
StreamSize renderStreamSize = VideoStreamRenderer.getSize();
int width = renderStreamSize.getWidth();
int height = renderStreamSize.getHeight();
Metodi e proprietà rendererView
Quando si crea un oggetto VideoStreamRendererView
è possibile specificare le ScalingMode
proprietà e mirrored
che verranno applicate a questa visualizzazione: la modalità di ridimensionamento può essere una delle proprietà 'CROP' | 'FIT'
VideoStreamRenderer remoteVideoRenderer = new VideoStreamRenderer(remoteVideoStream, appContext);
VideoStreamRendererView rendererView = remoteVideoRenderer.createView(new CreateViewOptions(ScalingMode.Fit));
Il rendererView creato può quindi essere collegato all'interfaccia utente dell'applicazione usando il frammento di codice seguente:
layout.addView(rendererView);
In seguito è possibile aggiornare la modalità di ridimensionamento richiamando updateScalingMode
l'API nell'oggetto RendererView con uno di ScalingMode.CROP | ScalingMode.FIT come argomento.
// Update the scale mode for this view.
rendererView.updateScalingMode(ScalingMode.CROP)
Configurare il sistema
Creare il progetto Xcode
In Xcode creare un nuovo progetto iOS e selezionare il modello Single View Application. Questa guida introduttiva usa il framework SwiftUI, quindi è consigliabile impostare Language su Swift e impostare Interface su SwiftUI.
Durante questa guida introduttiva non verranno creati test. Deselezionare la casella di controllo Includi test .
Installare il pacchetto e le dipendenze usando CocoaPods
Creare un podfile per l'applicazione, come nell'esempio seguente:
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 1.0.0' end
Eseguire
pod install
.Aprire
.xcworkspace
con Xcode.
Richiedere l'accesso al microfono
Per accedere al microfono del dispositivo, è necessario aggiornare l'elenco delle proprietà delle informazioni dell'app usando NSMicrophoneUsageDescription
. Impostare il valore associato su una stringa che verrà inclusa nella finestra di dialogo usata dal sistema per richiedere l'accesso dall'utente.
Fare clic con il pulsante destro del mouse sulla voce Info.plist dell'albero del progetto e quindi scegliere Apri come>codice sorgente. Aggiungere le righe seguenti nella sezione di primo livello <dict>
e quindi salvare il file.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Configurare il framework dell'app
Aprire il file ContentView.swift del progetto. Aggiungere una import
dichiarazione all'inizio del file per importare la AzureCommunicationCalling
libreria. Inoltre, importare AVFoundation
. Sarà necessario per le richieste di autorizzazione audio nel codice.
import AzureCommunicationCalling
import AVFoundation
Inizializzare CallAgent
Per creare un'istanza CallAgent
da CallClient
, è necessario usare un callClient.createCallAgent
metodo che restituisce in modo asincrono un CallAgent
oggetto dopo l'inizializzazione.
Per creare un client di chiamata, passare un CommunicationTokenCredential
oggetto :
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)
}
Passare l'oggetto CommunicationTokenCredential
creato a CallClient
e impostare il nome visualizzato:
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")
}
})
Gestire i dispositivi
Per iniziare a usare video con le chiamate, è necessario sapere come gestire i dispositivi. I dispositivi consentono di controllare cosa trasmette audio e video alla chiamata.
DeviceManager
consente di enumerare i dispositivi locali che possono essere usati in una chiamata per trasmettere flussi audio o video. Consente inoltre di richiedere l'autorizzazione da un utente per accedere a un microfono o a una fotocamera. È possibile accedere deviceManager
all'oggetto 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")
}
}
Enumerare i dispositivi locali
Per accedere ai dispositivi locali, è possibile usare i metodi di enumerazione nella gestione dispositivi. L'enumerazione è un'azione sincrona.
// enumerate local cameras
var localCameras = deviceManager.cameras // [VideoDeviceInfo, VideoDeviceInfo...]
Ottenere un'anteprima della fotocamera locale
È possibile usare Renderer
per iniziare a eseguire il rendering di un flusso dalla fotocamera locale. Questo flusso non verrà inviato ad altri partecipanti; è un feed di anteprima locale. Si tratta di un'azione asincrona.
let camera: VideoDeviceInfo = self.deviceManager!.cameras.first!
let localVideoStream = LocalVideoStream(camera: camera)
let localRenderer = try! VideoStreamRenderer(localVideoStream: localVideoStream)
self.view = try! localRenderer.createView()
Ottenere le proprietà di anteprima della fotocamera locale
Il renderer include un set di proprietà e metodi che consentono di controllare il rendering.
// 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()
Effettuare una chiamata 1:1 con video
Per ottenere un'istanza di Gestione dispositivi, vedere la sezione sulla gestione dei dispositivi.
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")
}
}
Eseguire il rendering dei flussi video dei partecipanti remoti
I partecipanti remoti possono avviare la condivisione di video o schermo durante una chiamata.
Gestire flussi di condivisione video o condivisione dello schermo di partecipanti remoti
Per elencare i flussi dei partecipanti remoti, esaminare le videoStreams
raccolte.
var remoteParticipantVideoStream = call.remoteParticipants[0].videoStreams[0]
Ottenere le proprietà del flusso video remoto
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
Eseguire il rendering dei flussi dei partecipanti remoti
Per avviare il rendering dei flussi partecipanti remoti, usare il codice seguente.
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)
Ottenere metodi e proprietà del renderer video remoto
// [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()
Configurare il sistema
Creare il progetto di Visual Studio
Per un'app UWP, in Visual Studio 2022 creare un nuovo progetto App vuota (Windows universale). Dopo aver immesso il nome del progetto, è possibile scegliere qualsiasi Windows SDK successivo alla versione 10.0.17763.0.
Per un'app WinUI 3, creare un nuovo progetto con il modello App vuota, In pacchetto (WinUI 3 in Desktop) per configurare un'app WinUI 3 a pagina singola. SDK per app di Windows versione 1.3 o successiva è necessario.
Installare il pacchetto e le dipendenze usando NuGet Gestione pacchetti
Le API e le librerie di Calling SDK sono disponibili pubblicamente tramite un pacchetto NuGet.
I passaggi seguenti illustrano come trovare, scaricare e installare il pacchetto NuGet di Calling SDK:
- Aprire Gestione pacchetti NuGet selezionando Strumenti>NuGet Gestione pacchetti> Gestisci pacchetti NuGet per la soluzione.
- Selezionare Sfoglia e quindi immettere
Azure.Communication.Calling.WindowsClient
nella casella di ricerca. - Assicurarsi che la casella di controllo Includi versione preliminare sia selezionata.
- Selezionare il
Azure.Communication.Calling.WindowsClient
pacchetto e quindi selezionareAzure.Communication.Calling.WindowsClient
1.4.0-beta.1 o una versione più recente. - Selezionare la casella di controllo corrispondente al progetto Servizi di comunicazione nella scheda a destra.
- Selezionare il pulsante Installa.
Richiedere l'accesso al microfono
L'app richiede l'accesso alla fotocamera per l'esecuzione corretta. Nelle app UWP, la funzionalità della fotocamera deve essere dichiarata nel file manifesto dell'app.
I passaggi seguenti esemplificano come ottenere questo risultato.
Solution Explorer
Nel pannello fare doppio clic sul file con.appxmanifest
estensione.- Fare clic sulla
Capabilities
scheda . - Selezionare la
Camera
casella di controllo dall'elenco delle funzionalità.
Creare pulsanti dell'interfaccia utente per posizionare e appendere la chiamata
Questa semplice app di esempio contiene due pulsanti. Uno per inserire la chiamata e un altro per appendere una chiamata inserita. La procedura seguente illustra come aggiungere questi pulsanti all'app.
Solution Explorer
Nel pannello fare doppio clic sul file denominatoMainPage.xaml
per UWP oMainWindows.xaml
per WinUI 3.- Nel pannello centrale cercare il codice XAML sotto l'anteprima dell'interfaccia utente.
- Modificare il codice XAML seguendo l'estratto seguente:
<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>
Configurazione dell'app con le API dell'SDK per chiamate
Le API di Calling SDK si trovano in due spazi dei nomi diversi. La procedura seguente informa il compilatore C# su questi spazi dei nomi che consentono a IntelliSense di Visual Studio di supportare lo sviluppo di codice.
Solution Explorer
Nel pannello fare clic sulla freccia sul lato sinistro del file denominatoMainPage.xaml
per UWP oMainWindows.xaml
per WinUI 3.- Fare doppio clic sul file denominato
MainPage.xaml.cs
oMainWindows.xaml.cs
. - Aggiungere i comandi seguenti nella parte inferiore delle istruzioni correnti
using
.
using Azure.Communication.Calling.WindowsClient;
Mantenere MainPage.xaml.cs
o MainWindows.xaml.cs
aprire. I passaggi successivi aggiungeranno altro codice.
Consenti interazioni con le app
I pulsanti dell'interfaccia utente aggiunti in precedenza devono operare sopra un elemento posizionato CommunicationCall
. Significa che un CommunicationCall
membro dati deve essere aggiunto alla MainPage
classe o MainWindow
.
Inoltre, per consentire la creazione CallAgent
dell'operazione asincrona, è necessario aggiungere un CallAgent
membro dati alla stessa classe.
Aggiungere i membri dati seguenti alla MainPage
classe o MainWindow
:
CallAgent callAgent;
CommunicationCall call;
Creare gestori di pulsanti
In precedenza, al codice XAML sono stati aggiunti due pulsanti dell'interfaccia utente. Il codice seguente aggiunge i gestori da eseguire quando un utente seleziona il pulsante. Il codice seguente deve essere aggiunto dopo i membri dati della sezione precedente.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// End the current call
}
Modello a oggetti
Le classi e le interfacce seguenti gestiscono alcune delle principali funzionalità della libreria client di chiamata Servizi di comunicazione di Azure per la piattaforma UWP.
Nome | Descrizione |
---|---|
CallClient |
CallClient è il punto di ingresso principale della libreria client chiamante. |
CallAgent |
Viene CallAgent utilizzato per avviare e partecipare alle chiamate. |
CommunicationCall |
L'oggetto CommunicationCall viene utilizzato per gestire le chiamate inserite o unite in join. |
CommunicationTokenCredential |
Viene CommunicationTokenCredential usato come credenziale del token per creare un'istanza di CallAgent . |
CallAgentOptions |
CallAgentOptions Contiene informazioni per identificare il chiamante. |
HangupOptions |
HangupOptions Informa se una chiamata deve essere terminata a tutti i partecipanti. |
Registrare il gestore dello schema video
Un componente dell'interfaccia utente, ad esempio MediaElement o MediaPlayerElement di XAML, è necessaria l'app che registra una configurazione per il rendering di feed video locali e remoti.
Aggiungere il contenuto seguente tra i Package
tag di Package.appxmanifest
:
<Extensions>
<Extension Category="windows.activatableClass.inProcessServer">
<InProcessServer>
<Path>RtmMvrUap.dll</Path>
<ActivatableClass ActivatableClassId="VideoN.VideoSchemeHandler" ThreadingModel="both" />
</InProcessServer>
</Extension>
</Extensions>
Inizializzare CallAgent
Per creare un'istanza CallAgent
da CallClient
, è necessario usare il CallClient.CreateCallAgentAsync
metodo che restituisce in modo asincrono un CallAgent
oggetto dopo l'inizializzazione.
Per creare CallAgent
, è necessario passare un CallTokenCredential
oggetto e un CallAgentOptions
oggetto . Tenere presente che CallTokenCredential
genera un'eccezione se viene passato un token in formato non valido.
Il codice seguente deve essere aggiunto all'interno e alla funzione helper da chiamare nell'inizializzazione dell'app.
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;
<AUTHENTICATION_TOKEN>
Modificare con un token di credenziali valido per la risorsa. Fare riferimento alla documentazione del token di accesso utente se è necessario creare un token di credenziali.
Effettuare una chiamata 1:1 con la videocamera
Gli oggetti necessari per la creazione di un CallAgent
oggetto sono ora pronti. È il momento di creare CallAgent
e inserire in modo asincrono una videochiamata.
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 } }
};
}
Anteprima della fotocamera locale
Facoltativamente, è possibile configurare l'anteprima della fotocamera locale. Il rendering del video può essere eseguito tramite MediaPlayerElement
:
<Grid>
<MediaPlayerElement x:Name="LocalVideo" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" AutoPlay="True" />
</Grid>
Per inizializzare l'anteprima MediaPlayerElement
locale:
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);
}
}
Eseguire il rendering del flusso della fotocamera remota
Configurare anche il gestore in risposta all'evento 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;
}
}
Avviare il rendering del flusso video remoto in 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;
}
}
Terminare una chiamata
Una volta inserita una chiamata, è necessario utilizzare il HangupAsync
metodo dell'oggetto CommunicationCall
per appendere la chiamata.
È inoltre necessario utilizzare un'istanza di HangupOptions
per informare se la chiamata deve essere terminata a tutti i partecipanti.
Il codice seguente deve essere aggiunto all'interno HangupButton_Click
di .
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;
}
}
}
}
Eseguire il codice
Assicurarsi che Visual Studio compili l'app per x64
o x86
ARM64
, quindi premere F5
per avviare l'esecuzione dell'app. Successivamente, fare clic sul CommunicationCall
pulsante per inserire una chiamata al chiamato definito.
Tenere presente che la prima volta che viene eseguita l'app, il sistema chiede all'utente di concedere l'accesso al microfono.
Passaggi successivi
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per