Inicio rápido: Incorporación de una aplicación de llamadas a un operador automático de Teams
En este inicio rápido, aprenderá a iniciar una llamada de un usuario de Azure Communication Services a un operador automático de Teams. Lo logrará con los pasos siguientes:
- Habilitar la federación de recurso de Azure Communication Services con el inquilino de Teams.
- Seleccione o cree el operador automático de Teams a través del Centro de Administración de Teams.
- Obtenga la dirección de correo electrónico del operador automático a través del Centro de Administración de Teams.
- Obtenga el id. de objeto del operador automático a través de Graph API.
- Iniciar una llamada con el SDK de llamadas de Azure Communication Services.
Si quiere ir directamente al final, puede descargar esta guía de inicio rápido como ejemplo desde GitHub.
Habilitar interoperabilidad en el inquilino de Teams
El usuario de Microsoft Entra con el rol de administrador de Teams puede ejecutar el cmdlet de PowerShell con el módulo de MicrosoftTeams para habilitar el recurso de Communication Services en el inquilino.
1. Preparar el módulo de Microsoft Teams
En primer lugar, abra PowerShell y valide la existencia del módulo de Teams con el siguiente comando:
Get-module *teams*
Si no ve el módulo MicrosoftTeams
, instálelo primero. Para instalar el módulo debe ejecutar PowerShell como administrador. Luego, ejecute el siguiente comando:
Install-Module -Name MicrosoftTeams
Se le informará sobre los módulos que se instalarán, que puede confirmar con una respuesta Y
o A
. Si el módulo está instalado pero no está actualizado, puede ejecutar el siguiente comando para actualizar el módulo:
Update-Module MicrosoftTeams
2. Conexión al módulo de Microsoft Teams
Cuando el módulo esté instalado y listo, puede conectarse al módulo MicrosoftTeams con el siguiente comando. Se le pedirá que inicie sesión con una ventana interactiva. La cuenta de usuario que va a usar debe tener permisos de administrador de Teams. De lo contrario, puede obtener una respuesta access denied
en los pasos siguientes.
Connect-MicrosoftTeams
3. Habilitar la configuración del inquilino
La interoperabilidad con los recursos de Communication Services se controla mediante la configuración de inquilinos y la directiva asignada. El inquilino de Teams tiene una configuración de inquilino único y los usuarios de Teams tienen asignada directiva global o directiva personalizada. Para más información, consulte Asignación de directivas en Teams.
Después de iniciar sesión correctamente, puede ejecutar el cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar el recurso de Communication Services en el inquilino. Reemplace el texto IMMUTABLE_RESOURCE_ID
por un identificador de recurso inmutable en el recurso de comunicación. Puede encontrar más detalles sobre cómo obtener esta información aquí.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar una directiva de inquilino
A cada usuario de Teams se le ha asignado un External Access Policy
que determina si los usuarios de Communication Services pueden llamar a este usuario de Teams. Use el cmdlet Set-CsExternalAccessPolicy para asegurarse de que la directiva asignada al usuario de Teams ha establecido EnableAcsFederationAccess
en $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Crear o seleccionar Operador automático de Teams
El operador automático de Teams es un sistema que proporciona un sistema de control de llamadas automatizado para las llamadas entrantes. Sirve de recepcionista virtual, permitiendo que los autores de llamadas se enruten automáticamente a la persona o departamento adecuados sin necesidad de un operador humano. Puede seleccionar un operador automático existente o crearlo a través del Centro de administración de Teams.
Obtenga más información sobre cómo crear un operador automático mediante el Centro de administración de Teams aquí.
Buscar el id. de objeto para el operador automático
Después de crear el operador automático, es necesario encontrar el id. de objeto correlacionado para usarlo más adelante para las llamadas. El id. de objeto está conectado a la cuenta de recursos que se adjuntó al operador automático: abra la pestaña Cuentas de recursos en Administración de Teams y busque el correo electrónico de la cuenta. Toda la información necesaria para la cuenta de recursos se puede encontrar en Explorador de Microsoft Graph utilizando este correo electrónico en la búsqueda.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
En los resultados, encontraremos el campo "Id."
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Prerrequisitos
- Obtenga una cuenta de Azure con una suscripción activa. Cree una cuenta gratuita.
- Versiones Active LTS y Maintenance LTS de Node.js (8.11.1 y 10.14.1)
- Cree un recurso activo de Communication Services. Cree un recurso de Communication Services.
Instalación
Creación de una aplicación Node.js
Abra la ventana de comandos o el terminal, cree un nuevo directorio para la aplicación y vaya al directorio.
mkdir calling-quickstart && cd calling-quickstart
Instalar el paquete
Use el comando npm install
para instalar el SDK de llamadas de Azure Communication Services para JavaScript.
Importante
En este inicio rápido se usa la versión next
del SDK de llamadas de Azure Communication Services.
npm install @azure/communication-common@next --save
npm install @azure/communication-calling@next --save
Instalación del marco de la aplicación
Esta guía de inicio rápido usa webpack para agrupar los recursos de la aplicación. Ejecute el siguiente comando para instalar los paquetes npm webpack
, webpack-cli
y webpack-dev-server
, y los enumera como dependencias de desarrollo en el archivo package.json
:
npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev
Cree un archivo index.html
en el directorio raíz del proyecto. Este archivo lo usaremos para configurar un diseño básico que permitirá al usuario realizar una llamada de video 1:1.
Este es el código:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Azure Communication Services - Calling Web SDK</title>
</head>
<body>
<h4>Azure Communication Services - Calling Web SDK</h4>
<input id="user-access-token"
type="text"
placeholder="User access token"
style="margin-bottom:1em; width: 500px;"/>
<button id="initialize-teams-call-agent" type="button">Initialize Call Agent</button>
<br>
<br>
<input id="application-object-id"
type="text"
placeholder="Enter application objectId identity in format: 'APP_GUID'"
style="margin-bottom:1em; width: 500px; display: block;"/>
<button id="start-call-button" type="button" disabled="true">Start Call</button>
<button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
<button id="accept-call-button" type="button" disabled="true">Accept Call</button>
<button id="start-video-button" type="button" disabled="true">Start Video</button>
<button id="stop-video-button" type="button" disabled="true">Stop Video</button>
<br>
<br>
<div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
<br>
<div id="remoteVideoContainer" style="width: 40%;" hidden>Remote participants' video streams:</div>
<br>
<div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
<!-- points to the bundle generated from client.js -->
<script src="./main.js"></script>
</body>
</html>
Modelo de objetos del SDK web de llamada de Azure Communication Services
Las siguientes clases e interfaces administran algunas de las características principales de Calling SDK de Azure Communication Services:
Nombre | Descripción |
---|---|
CallClient |
El punto de entrada principal al SDK de llamadas. |
CallAgent |
Se utiliza para iniciar y administrar las llamadas. |
DeviceManager |
Se usa para administrar dispositivos multimedia. |
Call |
Se usa para representar una llamada. |
LocalVideoStream |
Se usa para crear una secuencia de vídeo local para un dispositivo de cámara en el sistema local. |
RemoteParticipant |
Se usa para representar a un participante remoto de la llamada. |
RemoteVideoStream |
Se usa para representar una secuencia de vídeo remota desde un participante remoto. |
Cree un archivo en el directorio raíz del proyecto denominado client.js
que contendrá la lógica de la aplicación para esta guía de inicio rápido. Agregue el código siguiente a client.js:
// Make sure to install the necessary dependencies
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the log level and output
setLogLevel('verbose');
AzureLogger.log = (...args) => {
console.log(...args);
};
// Calling web sdk objects
let callAgent;
let deviceManager;
let call;
let incomingCall;
let localVideoStream;
let localVideoStreamRenderer;
// UI widgets
let userAccessToken = document.getElementById('user-access-token');
let applicationObjectId = document.getElementById('application-object-id');
let initializeCallAgentButton = document.getElementById('initialize-teams-call-agent');
let startCallButton = document.getElementById('start-call-button');
let hangUpCallButton = document.getElementById('hangup-call-button');
let acceptCallButton = document.getElementById('accept-call-button');
let startVideoButton = document.getElementById('start-video-button');
let stopVideoButton = document.getElementById('stop-video-button');
let connectedLabel = document.getElementById('connectedLabel');
let remoteVideoContainer = document.getElementById('remoteVideoContainer');
let localVideoContainer = document.getElementById('localVideoContainer');
/**
* Create an instance of CallClient. Initialize a CallAgent instance with a AzureCommunicationTokenCredential via created CallClient. CallAgent enables us to make outgoing calls and receive incoming calls.
* You can then use the CallClient.getDeviceManager() API instance to get the DeviceManager.
*/
initializeCallAgentButton.onclick = async () => {
try {
const callClient = new CallClient();
tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim());
callAgent = await callClient.createCallAgent(tokenCredential)
// Set up a camera device to use.
deviceManager = await callClient.getDeviceManager();
await deviceManager.askDevicePermission({ video: true });
await deviceManager.askDevicePermission({ audio: true });
// Listen for an incoming call to accept.
callAgent.on('incomingCall', async (args) => {
try {
incomingCall = args.incomingCall;
acceptCallButton.disabled = false;
startCallButton.disabled = true;
} catch (error) {
console.error(error);
}
});
startCallButton.disabled = false;
initializeCallAgentButton.disabled = true;
} catch(error) {
console.error(error);
}
}
/**
* Place a 1:1 outgoing video call to an Teams Auto attendant
* Add an event listener to initiate a call when the `startCallButton` is selected.
* Enumerate local cameras using the deviceManager `getCameraList` API.
* In this quickstart, we're using the first camera in the collection. Once the desired camera is selected, a
* LocalVideoStream instance will be constructed and passed within `videoOptions` as an item within the
* localVideoStream array to the call method. When the call connects, your application will be sending a video stream to the other participant.
*/
startCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = callAgent.startCall([{ teamsAppId: applicationObjectId.value.trim(), cloud:"public" }], { videoOptions: videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
/**
* Accepting an incoming call with a video
* Add an event listener to accept a call when the `acceptCallButton` is selected.
* You can accept incoming calls after subscribing to the `CallAgent.on('incomingCall')` event.
* You can pass the local video stream to accept the call with the following code.
*/
acceptCallButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
call = await incomingCall.accept({ videoOptions });
// Subscribe to the call's properties and events.
subscribeToCall(call);
} catch (error) {
console.error(error);
}
}
// Subscribe to a call obj.
// Listen for property changes and collection updates.
subscribeToCall = (call) => {
try {
// Inspect the initial call.id value.
console.log(`Call Id: ${call.id}`);
//Subscribe to call's 'idChanged' event for value changes.
call.on('idChanged', () => {
console.log(`Call ID changed: ${call.id}`);
});
// Inspect the initial call.state value.
console.log(`Call state: ${call.state}`);
// Subscribe to call's 'stateChanged' event for value changes.
call.on('stateChanged', async () => {
console.log(`Call state changed: ${call.state}`);
if(call.state === 'Connected') {
connectedLabel.hidden = false;
acceptCallButton.disabled = true;
startCallButton.disabled = true;
hangUpCallButton.disabled = false;
startVideoButton.disabled = false;
stopVideoButton.disabled = false;
} else if (call.state === 'Disconnected') {
connectedLabel.hidden = true;
startCallButton.disabled = false;
hangUpCallButton.disabled = true;
startVideoButton.disabled = true;
stopVideoButton.disabled = true;
console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
}
});
call.on('isLocalVideoStartedChanged', () => {
console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
});
console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`);
call.localVideoStreams.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
call.on('localVideoStreamsUpdated', e => {
e.added.forEach(async (lvs) => {
localVideoStream = lvs;
await displayLocalVideoStream();
});
e.removed.forEach(lvs => {
removeLocalVideoStream();
});
});
// Inspect the call's current remote participants and subscribe to them.
call.remoteParticipants.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant);
});
// Subscribe to the call's 'remoteParticipantsUpdated' event to be
// notified when new participants are added to the call or removed from the call.
call.on('remoteParticipantsUpdated', e => {
// Subscribe to new remote participants that are added to the call.
e.added.forEach(remoteParticipant => {
subscribeToRemoteParticipant(remoteParticipant)
});
// Unsubscribe from participants that are removed from the call
e.removed.forEach(remoteParticipant => {
console.log('Remote participant removed from the call.');
});
});
} catch (error) {
console.error(error);
}
}
// Subscribe to a remote participant obj.
// Listen for property changes and collection updates.
subscribeToRemoteParticipant = (remoteParticipant) => {
try {
// Inspect the initial remoteParticipant.state value.
console.log(`Remote participant state: ${remoteParticipant.state}`);
// Subscribe to remoteParticipant's 'stateChanged' event for value changes.
remoteParticipant.on('stateChanged', () => {
console.log(`Remote participant state changed: ${remoteParticipant.state}`);
});
// Inspect the remoteParticipants's current videoStreams and subscribe to them.
remoteParticipant.videoStreams.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
// notified when the remoteParticipant adds new videoStreams and removes video streams.
remoteParticipant.on('videoStreamsUpdated', e => {
// Subscribe to newly added remote participant's video streams.
e.added.forEach(remoteVideoStream => {
subscribeToRemoteVideoStream(remoteVideoStream)
});
// Unsubscribe from newly removed remote participants' video streams.
e.removed.forEach(remoteVideoStream => {
console.log('Remote participant video stream was removed.');
})
});
} catch (error) {
console.error(error);
}
}
/**
* Subscribe to a remote participant's remote video stream obj.
* You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property
* changes to 'true' a remote participant is sending a stream. Whenever the availability of a remote stream changes
* you can choose to destroy the whole 'Renderer' a specific 'RendererView' or keep them. Displaying RendererView without a video stream will result in a blank video frame.
*/
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
// Create a video stream renderer for the remote video stream.
let videoStreamRenderer = new VideoStreamRenderer(remoteVideoStream);
let view;
const renderVideo = async () => {
try {
// Create a renderer view for the remote video stream.
view = await videoStreamRenderer.createView();
// Attach the renderer view to the UI.
remoteVideoContainer.hidden = false;
remoteVideoContainer.appendChild(view.target);
} catch (e) {
console.warn(`Failed to createView, reason=${e.message}, code=${e.code}`);
}
}
remoteVideoStream.on('isAvailableChanged', async () => {
// Participant has switched video on.
if (remoteVideoStream.isAvailable) {
await renderVideo();
// Participant has switched video off.
} else {
if (view) {
view.dispose();
view = undefined;
}
}
});
// Participant has video on initially.
if (remoteVideoStream.isAvailable) {
await renderVideo();
}
}
// Start your local video stream.
// This will send your local video stream to remote participants so they can view it.
startVideoButton.onclick = async () => {
try {
const localVideoStream = await createLocalVideoStream();
await call.startVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
// Stop your local video stream.
// This will stop your local video stream from being sent to remote participants.
stopVideoButton.onclick = async () => {
try {
await call.stopVideo(localVideoStream);
} catch (error) {
console.error(error);
}
}
/**
* To render a LocalVideoStream, you need to create a new instance of VideoStreamRenderer, and then
* create a new VideoStreamRendererView instance using the asynchronous createView() method.
* You may then attach view.target to any UI element.
*/
// Create a local video stream for your camera device
createLocalVideoStream = async () => {
const camera = (await deviceManager.getCameras())[0];
if (camera) {
return new LocalVideoStream(camera);
} else {
console.error(`No camera device found on the system`);
}
}
// Display your local video stream preview in your UI
displayLocalVideoStream = async () => {
try {
localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
const view = await localVideoStreamRenderer.createView();
localVideoContainer.hidden = false;
localVideoContainer.appendChild(view.target);
} catch (error) {
console.error(error);
}
}
// Remove your local video stream preview from your UI
removeLocalVideoStream = async() => {
try {
localVideoStreamRenderer.dispose();
localVideoContainer.hidden = true;
} catch (error) {
console.error(error);
}
}
// End the current call
hangUpCallButton.addEventListener("click", async () => {
// end the current call
await call.hangUp();
});
Agregar el código del servidor local del paquete web
Cree un archivo en el directorio raíz del proyecto denominado webpack.config.js que contendrá la lógica de la aplicación para esta guía de inicio rápido. Agregue el siguiente código a webpack.config.js:
const path = require('path');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: 'development',
entry: './client.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
devServer: {
static: {
directory: path.join(__dirname, './')
},
},
plugins: [
new CopyPlugin({
patterns: [
'./index.html'
]
}),
]
};
Ejecución del código
Utilice webpack-dev-server
para compilar y ejecutar la aplicación. Ejecute el siguiente comando para agrupar el host de aplicación en un servidor web local:
npx webpack serve --config webpack.config.js
Pasos manuales para configurar la llamada:
- Abra el explorador web y vaya a http://localhost:8080/.
- Escribir un token de acceso de usuario válido. Consulte la documentación relativa al token de acceso de usuario si aún no tiene ningún token de acceso disponible para utilizarlo.
- Haga clic en los botones "Inicializar agente de llamada".
- Escriba el id. de objeto del operador automático y seleccione el botón "Iniciar llamada". La aplicación iniciará la llamada saliente al operador automático con el id. de objeto especificado.
- La llamada está conectada al operador automático.
- El usuario de Communication Services se enruta a través del operador automático en función de su configuración.
Importante
Esta característica de Azure Communication Services se encuentra actualmente en versión preliminar.
Las API y los SDK en versión preliminar se proporcionan sin contrato de nivel de servicio. Se recomienda no usarlos para las cargas de trabajo de producción. Es posible que algunas características no sean compatibles o que sus funcionalidades estén limitadas.
Para obtener más información, consulte Términos de uso complementarios para las Versiones preliminares de Microsoft Azure.
En este inicio rápido, aprenderá a iniciar una llamada de un usuario de Azure Communication Services a un operador automático de Teams. Lo logrará con los pasos siguientes:
- Habilitar la federación de recurso de Azure Communication Services con el inquilino de Teams.
- Seleccione o cree el operador automático de Teams a través del Centro de Administración de Teams.
- Obtenga la dirección de correo electrónico del operador automático a través del Centro de Administración de Teams.
- Obtenga el id. de objeto del operador automático a través de Graph API.
- Iniciar una llamada con el SDK de llamadas de Azure Communication Services.
Si quiere ir directamente al final, puede descargar esta guía de inicio rápido como ejemplo desde GitHub.
Habilitar interoperabilidad en el inquilino de Teams
El usuario de Microsoft Entra con el rol de administrador de Teams puede ejecutar el cmdlet de PowerShell con el módulo de MicrosoftTeams para habilitar el recurso de Communication Services en el inquilino.
1. Preparar el módulo de Microsoft Teams
En primer lugar, abra PowerShell y valide la existencia del módulo de Teams con el siguiente comando:
Get-module *teams*
Si no ve el módulo MicrosoftTeams
, instálelo primero. Para instalar el módulo debe ejecutar PowerShell como administrador. Luego, ejecute el siguiente comando:
Install-Module -Name MicrosoftTeams
Se le informará sobre los módulos que se instalarán, que puede confirmar con una respuesta Y
o A
. Si el módulo está instalado pero no está actualizado, puede ejecutar el siguiente comando para actualizar el módulo:
Update-Module MicrosoftTeams
2. Conexión al módulo de Microsoft Teams
Cuando el módulo esté instalado y listo, puede conectarse al módulo MicrosoftTeams con el siguiente comando. Se le pedirá que inicie sesión con una ventana interactiva. La cuenta de usuario que va a usar debe tener permisos de administrador de Teams. De lo contrario, puede obtener una respuesta access denied
en los pasos siguientes.
Connect-MicrosoftTeams
3. Habilitar la configuración del inquilino
La interoperabilidad con los recursos de Communication Services se controla mediante la configuración de inquilinos y la directiva asignada. El inquilino de Teams tiene una configuración de inquilino único y los usuarios de Teams tienen asignada directiva global o directiva personalizada. Para más información, consulte Asignación de directivas en Teams.
Después de iniciar sesión correctamente, puede ejecutar el cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar el recurso de Communication Services en el inquilino. Reemplace el texto IMMUTABLE_RESOURCE_ID
por un identificador de recurso inmutable en el recurso de comunicación. Puede encontrar más detalles sobre cómo obtener esta información aquí.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar una directiva de inquilino
A cada usuario de Teams se le ha asignado un External Access Policy
que determina si los usuarios de Communication Services pueden llamar a este usuario de Teams. Use el cmdlet Set-CsExternalAccessPolicy para asegurarse de que la directiva asignada al usuario de Teams ha establecido EnableAcsFederationAccess
en $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Crear o seleccionar Operador automático de Teams
El operador automático de Teams es un sistema que proporciona un sistema de control de llamadas automatizado para las llamadas entrantes. Sirve de recepcionista virtual, permitiendo que los autores de llamadas se enruten automáticamente a la persona o departamento adecuados sin necesidad de un operador humano. Puede seleccionar un operador automático existente o crearlo a través del Centro de administración de Teams.
Obtenga más información sobre cómo crear un operador automático mediante el Centro de administración de Teams aquí.
Buscar el id. de objeto para el operador automático
Después de crear el operador automático, es necesario encontrar el id. de objeto correlacionado para usarlo más adelante para las llamadas. El id. de objeto está conectado a la cuenta de recursos que se adjuntó al operador automático: abra la pestaña Cuentas de recursos en Administración de Teams y busque el correo electrónico de la cuenta. Toda la información necesaria para la cuenta de recursos se puede encontrar en Explorador de Microsoft Graph utilizando este correo electrónico en la búsqueda.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
En los resultados, encontraremos el campo "Id."
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usarlo en la aplicación que realiza la llamada, es necesario agregar un prefijo a este identificador. Actualmente se admite lo siguiente:
- Operador automático de nube pública:
28:orgid:<id>
- Operador automático de nube gubernamental:
28:gcch:<id>
Requisitos previos
Una cuenta de Azure con una suscripción activa. Cree una cuenta gratuita.
Android Studio, para crear la aplicación de Android.
Un recurso de Communication Services implementado. Cree un recurso de Communication Services.
Un token de acceso de usuario para su instancia de Azure Communication Services. También puede usar la CLI de Azure y ejecutar el comando siguiente con la cadena de conexión para crear un usuario y un token de acceso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para más información, consulte Uso de la CLI de Azure para crear y administrar tokens de acceso.
Compatibilidad mínima con las aplicaciones de llamada de Teams: 2.12.0-beta.1
Instalación
Creación de una aplicación de Android con una actividad vacía
En Android Studio, seleccione Start a new Android Studio project (Iniciar un nuevo proyecto de Android Studio).
Seleccione la plantilla de proyecto "Actividad de vistas vacía" en "Teléfono y tableta".
Seleccione el SDK mínimo de la "API 26: Android 8.0 (Oreo)" o una versión posterior.
Instalar el paquete
Busque el nivel de proyecto settings.gradle.kts
y asegúrese de comprobar mavenCentral()
en la lista de repositorios en pluginManagement
y dependencyResolutionManagement
.
pluginManagement {
repositories {
...
mavenCentral()
...
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
...
mavenCentral()
}
}
A continuación, en el nivel de módulo build.gradle, agregue las siguientes líneas a las secciones de dependencias y Android.
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
...
implementation ("com.azure.android:azure-communication-calling:2.6.0")
...
}
Adición de permisos al manifiesto de aplicación
Para solicitar los permisos necesarios para realizar una llamada, primero se deben declarar en el manifiesto de aplicación (app/src/main/AndroidManifest.xml
). Reemplace el contenido del archivo por el código siguiente:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.contoso.acsquickstart">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--Our Calling SDK depends on the Apache HTTP SDK.
When targeting Android SDK 28+, this library needs to be explicitly referenced.
See https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Configuración del diseño de la aplicación
Se necesitan dos entradas: una entrada de texto para el identificador del destinatario y un botón para realizar la llamada. Estas entradas se pueden agregar a través del diseñador o editando el XML de diseño. Cree un botón con un identificador de call_button
y una entrada de texto de callee_id
. Vaya hasta (app/src/main/res/layout/activity_main.xml
) y reemplace el contenido del archivo con el código siguiente:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${launchApp}">
<EditText
android:id="@+id/callee_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Callee Id"
android:inputType="textPersonName"
android:layout_marginTop="100dp"
android:layout_marginHorizontal="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="46dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/call_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Call" />
<Button
android:id="@+id/hangup_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hangup" />
</LinearLayout>
<TextView
android:id="@+id/status_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Creación de los enlaces y scaffolding de actividades principales
Con el diseño creado, se pueden agregar los enlaces, así como el scaffolding básico de la actividad. La actividad administra la solicitud de los permisos en tiempo de ejecución, la creación del agente de llamadas y la realización de llamadas cuando se presione el botón. El método onCreate
se reemplazará para invocar getAllPermissions
y createAgent
, así como para agregar los enlaces para el botón de llamada. Esto solo ocurre una vez cuando se crea la actividad. Para obtener más información sobre onCreate
, consulte la guía Descripción del ciclo de vida de la actividad.
Vaya a MainActivity.java y reemplace el contenido por el siguiente código:
package com.contoso.acsquickstart;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import com.azure.android.communication.common.CommunicationIdentifier;
import com.azure.android.communication.common.CommunicationUserIdentifier;
import com.azure.android.communication.calling.Call;
import com.azure.android.communication.calling.CallAgent;
import com.azure.android.communication.calling.CallClient;
import com.azure.android.communication.calling.HangUpOptions;
import com.azure.android.communication.common.CommunicationTokenCredential;
import com.azure.android.communication.calling.StartCallOptions;
public class MainActivity extends AppCompatActivity {
private static final String[] allPermissions = new String[] { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE };
private static final String UserToken = "<User_Access_Token>";
TextView statusBar;
private CallAgent agent;
private Call call;
private Button callButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callButton = findViewById(R.id.call_button);
getAllPermissions();
createAgent();
callButton.setOnClickListener(l -> startCall());
Button hangupButton = findViewById(R.id.hangup_button);
hangupButton.setOnClickListener(l -> endCall());
statusBar = findViewById(R.id.status_bar);
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
}
/**
* Start a call
*/
private void startCall() {
if (UserToken.startsWith("<")) {
Toast.makeText(this, "Please enter token in source code", Toast.LENGTH_SHORT).show();
return;
}
EditText calleeIdView = findViewById(R.id.callee_id);
String calleeId = calleeIdView.getText().toString();
if (calleeId.isEmpty()) {
Toast.makeText(this, "Please enter callee", Toast.LENGTH_SHORT).show();
return;
}
ArrayList<CommunicationIdentifier> participants = new ArrayList<>();
participants.add(new MicrosoftTeamsAppIdentifier(calleeId));
StartCallOptions options = new StartCallOptions();
call = agent.startCall(
getApplicationContext(),
participants,
options);
call.addOnStateChangedListener(p -> setStatus(call.getState().toString()));
}
/**
* Ends the call previously started
*/
private void endCall() {
try {
call.hangUp(new HangUpOptions()).get();
} catch (ExecutionException | InterruptedException e) {
Toast.makeText(this, "Unable to hang up call", Toast.LENGTH_SHORT).show();
}
}
/**
* Create the call agent
*/
private void createAgent() {
try {
CommunicationTokenCredential credential = new CommunicationTokenCredential(UserToken);
agent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
} catch (Exception ex) {
Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
}
}
/**
* Ensure all permissions were granted, otherwise inform the user permissions are missing.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
boolean allPermissionsGranted = true;
for (int result : grantResults) {
allPermissionsGranted &= (result == PackageManager.PERMISSION_GRANTED);
}
if (!allPermissionsGranted) {
Toast.makeText(this, "All permissions are needed to make the call.", Toast.LENGTH_LONG).show();
finish();
}
}
/**
* Shows message in the status bar
*/
private void setStatus(String status) {
runOnUiThread(() -> statusBar.setText(status));
}
}
Solicitud de permisos en tiempo de ejecución
Para Android 6.0 y las versiones posteriores (nivel de API 23) y targetSdkVersion
23 o las versiones posteriores, los permisos se conceden en tiempo de ejecución en lugar de cuando se instala la aplicación. A fin de admitirlo, getAllPermissions
se puede implementar para llamar a ActivityCompat.checkSelfPermission
y ActivityCompat.requestPermissions
para cada permiso necesario.
/**
* Request each required permission if the app doesn't already have it.
*/
private void getAllPermissions() {
ArrayList<String> permissionsToAskFor = new ArrayList<>();
for (String permission : allPermissions) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
permissionsToAskFor.add(permission);
}
}
if (!permissionsToAskFor.isEmpty()) {
ActivityCompat.requestPermissions(this, permissionsToAskFor.toArray(new String[0]), 1);
}
}
Nota
Al diseñar la aplicación, tenga en cuenta cuándo deben solicitarse estos permisos. Se deben solicitar a medida que sean necesarios, pero no con anterioridad. Para más información, consulte la Guía de permisos de Android.
Modelo de objetos
Las siguientes clases e interfaces controlan algunas de las características principales del SDK de llamadas de Azure Communication Services:
Nombre | Descripción |
---|---|
CallClient |
CallClient es el punto de entrada principal al SDK de llamada. |
CallAgent |
CallAgent se utiliza para iniciar y administrar las llamadas. |
CommunicationTokenCredential |
CommunicationTokenCredential se usa como la credencial del token para crear una instancia de CallAgent . |
CommunicationIdentifier |
CommunicationIdentifier se usa como otro tipo de participante que podría formar parte de una llamada. |
Creación de un agente a partir del token de acceso de usuario
Con un token de usuario, se puede crear una instancia del agente de llamadas autenticado. Por lo general, este token se genera desde un servicio con autenticación específica para la aplicación. Para obtener más información sobre los tokens de acceso de usuario, consulte la guía Tokens de acceso de usuario.
En el inicio rápido, reemplace <User_Access_Token>
por un token de acceso de usuario generado para el recurso de Azure Communication Service.
/**
* Create the call agent for placing calls
*/
private void createAgent() {
String userToken = "<User_Access_Token>";
try {
CommunicationTokenCredential credential = new CommunicationTokenCredential(userToken);
callAgent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
} catch (Exception ex) {
Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
}
}
Ejecución del código
Ahora se puede iniciar la aplicación con el botón "Ejecutar aplicación" de la barra de herramientas.
Pasos manuales para configurar la llamada:
- Inicie la aplicación mediante Android Studio.
- Escriba el id. de objeto del operador automático (con prefijo) y seleccione el botón "Iniciar llamada". La aplicación iniciará la llamada saliente al operador automático con el id. de objeto especificado.
- La llamada está conectada al operador automático.
- El usuario de Communication Services se enruta a través del operador automático en función de su configuración.
Importante
Esta característica de Azure Communication Services se encuentra actualmente en versión preliminar.
Las API y los SDK en versión preliminar se proporcionan sin contrato de nivel de servicio. Se recomienda no usarlos para las cargas de trabajo de producción. Es posible que algunas características no sean compatibles o que sus funcionalidades estén limitadas.
Para obtener más información, consulte Términos de uso complementarios para las Versiones preliminares de Microsoft Azure.
En este inicio rápido, aprenderá a iniciar una llamada de un usuario de Azure Communication Services a un operador automático de Teams. Lo logrará con los pasos siguientes:
- Habilitar la federación de recurso de Azure Communication Services con el inquilino de Teams.
- Seleccione o cree el operador automático de Teams a través del Centro de Administración de Teams.
- Obtenga la dirección de correo electrónico del operador automático a través del Centro de Administración de Teams.
- Obtenga el id. de objeto del operador automático a través de Graph API.
- Iniciar una llamada con el SDK de llamadas de Azure Communication Services.
Si quiere ir directamente al final, puede descargar esta guía de inicio rápido como ejemplo desde GitHub.
Habilitar interoperabilidad en el inquilino de Teams
El usuario de Microsoft Entra con el rol de administrador de Teams puede ejecutar el cmdlet de PowerShell con el módulo de MicrosoftTeams para habilitar el recurso de Communication Services en el inquilino.
1. Preparar el módulo de Microsoft Teams
En primer lugar, abra PowerShell y valide la existencia del módulo de Teams con el siguiente comando:
Get-module *teams*
Si no ve el módulo MicrosoftTeams
, instálelo primero. Para instalar el módulo debe ejecutar PowerShell como administrador. Luego, ejecute el siguiente comando:
Install-Module -Name MicrosoftTeams
Se le informará sobre los módulos que se instalarán, que puede confirmar con una respuesta Y
o A
. Si el módulo está instalado pero no está actualizado, puede ejecutar el siguiente comando para actualizar el módulo:
Update-Module MicrosoftTeams
2. Conexión al módulo de Microsoft Teams
Cuando el módulo esté instalado y listo, puede conectarse al módulo MicrosoftTeams con el siguiente comando. Se le pedirá que inicie sesión con una ventana interactiva. La cuenta de usuario que va a usar debe tener permisos de administrador de Teams. De lo contrario, puede obtener una respuesta access denied
en los pasos siguientes.
Connect-MicrosoftTeams
3. Habilitar la configuración del inquilino
La interoperabilidad con los recursos de Communication Services se controla mediante la configuración de inquilinos y la directiva asignada. El inquilino de Teams tiene una configuración de inquilino único y los usuarios de Teams tienen asignada directiva global o directiva personalizada. Para más información, consulte Asignación de directivas en Teams.
Después de iniciar sesión correctamente, puede ejecutar el cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar el recurso de Communication Services en el inquilino. Reemplace el texto IMMUTABLE_RESOURCE_ID
por un identificador de recurso inmutable en el recurso de comunicación. Puede encontrar más detalles sobre cómo obtener esta información aquí.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar una directiva de inquilino
A cada usuario de Teams se le ha asignado un External Access Policy
que determina si los usuarios de Communication Services pueden llamar a este usuario de Teams. Use el cmdlet Set-CsExternalAccessPolicy para asegurarse de que la directiva asignada al usuario de Teams ha establecido EnableAcsFederationAccess
en $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Crear o seleccionar Operador automático de Teams
El operador automático de Teams es un sistema que proporciona un sistema de control de llamadas automatizado para las llamadas entrantes. Sirve de recepcionista virtual, permitiendo que los autores de llamadas se enruten automáticamente a la persona o departamento adecuados sin necesidad de un operador humano. Puede seleccionar un operador automático existente o crearlo a través del Centro de administración de Teams.
Obtenga más información sobre cómo crear un operador automático mediante el Centro de administración de Teams aquí.
Buscar el id. de objeto para el operador automático
Después de crear el operador automático, es necesario encontrar el id. de objeto correlacionado para usarlo más adelante para las llamadas. El id. de objeto está conectado a la cuenta de recursos que se adjuntó al operador automático: abra la pestaña Cuentas de recursos en Administración de Teams y busque el correo electrónico de la cuenta. Toda la información necesaria para la cuenta de recursos se puede encontrar en Explorador de Microsoft Graph utilizando este correo electrónico en la búsqueda.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
En los resultados, encontraremos el campo "Id."
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usarlo en la aplicación que realiza la llamada, es necesario agregar un prefijo a este identificador. Actualmente se admite lo siguiente:
- Operador automático de nube pública:
28:orgid:<id>
- Operador automático de nube gubernamental:
28:gcch:<id>
Requisitos previos
Obtenga una cuenta de Azure con una suscripción activa. Cree una cuenta gratuita.
Mac con Xcode, junto con un certificado de desarrollador válido instalado en el Llavero.
Un recurso de Communication Services implementado. Cree un recurso de Communication Services. Debe registrar la cadena de conexión para esta guía de inicio rápido.
Un token de acceso de usuario para su instancia de Azure Communication Services. También puede usar la CLI de Azure y ejecutar el comando siguiente con la cadena de conexión para crear un usuario y un token de acceso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para más información, consulte Uso de la CLI de Azure para crear y administrar tokens de acceso.
Compatibilidad mínima con las aplicaciones de llamada de Teams: 2.14.0-beta.1
Instalación
Creación del proyecto de Xcode
En Xcode, cree un proyecto de iOS y seleccione la plantilla App (Aplicación). En este tutorial se usa el marco SwiftUI, por lo que debe establecer Lenguaje en Swift e Interfaz de usuario en SwiftUI. Durante este inicio rápido, no va a crear pruebas. No dude en desactivar Incluir pruebas.
Instalación del paquete y las dependencias con CocoaPods
Para crear un archivo Podfile para la aplicación, abra el terminal, vaya a la carpeta del proyecto y ejecute:
pod init
Agregue el código siguiente al podfile y guárdelo (Asegúrese de que "target" coincide con el nombre del proyecto):
platform :ios, '13.0' use_frameworks! target 'AzureCommunicationCallingSample' do pod 'AzureCommunicationCalling', '~> 2.14.0-beta.1' end
Ejecute
pod install
.Abra
.xcworkspace
con Xcode.
Solicitud de acceso al micrófono
Para acceder al micrófono del dispositivo, debe actualizar la lista de propiedades de información de la aplicación con el elemento NSMicrophoneUsageDescription
. Puede establecer el valor asociado al elemento string
que se incluirá en el cuadro de diálogo que el sistema usa para solicitar acceso al usuario.
Haga clic con el botón derecho en la entrada Info.plist
del árbol del proyecto y seleccione Abrir como>Código fuente. Agregue las líneas siguientes a la sección <dict>
de nivel superior y guarde el archivo.
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>
Instalación del marco de la aplicación
Abra el archivo ContentView.swift del proyecto y agregue una declaración import
en la parte superior del archivo para importar el elemento AzureCommunicationCalling library
. Además, importe AVFoundation
; lo necesitaremos para la solicitud de permiso de audio en el código.
import AzureCommunicationCalling
import AVFoundation
Reemplace la implementación de la estructura ContentView
por algunos controles de interfaz de usuario simples que permitan a los usuarios iniciar y finalizar una llamada. En este inicio rápido, asociaremos la lógica de negocios a estos controles.
struct ContentView: View {
@State var callee: String = ""
@State var callClient: CallClient?
@State var callAgent: CallAgent?
@State var call: Call?
var body: some View {
NavigationView {
Form {
Section {
TextField("Who would you like to call?", text: $callee)
Button(action: startCall) {
Text("Start Call")
}.disabled(callAgent == nil)
Button(action: endCall) {
Text("End Call")
}.disabled(call == nil)
}
}
.navigationBarTitle("Calling Quickstart")
}.onAppear {
// Initialize call agent
}
}
func startCall() {
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
// Add start call logic
}
}
}
func endCall() {
// Add end call logic
}
}
Modelo de objetos
Las siguientes clases e interfaces controlan algunas de las características principales del SDK de llamadas de Azure Communication Services:
Nombre | Descripción |
---|---|
CallClient |
CallClient es el punto de entrada principal al SDK de llamada. |
CallAgent |
CallAgent se utiliza para iniciar y administrar las llamadas. |
CommunicationTokenCredential |
CommunicationTokenCredential se usa como la credencial del token para crear una instancia de CallAgent . |
CommunicationUserIdentifier |
CommunicationUserIdentifier se usa para representar la identidad del usuario, que puede ser una de las opciones siguientes: CommunicationUserIdentifier , PhoneNumberIdentifier o CallingApplication. |
Autenticar el cliente
Inicialice una instancia de CallAgent
con un token de acceso de usuario que nos permita realizar y recibir llamadas.
En el código siguiente, tendrá que reemplazar <USER ACCESS TOKEN>
por un token de acceso de usuario válido para el recurso. Consulte la documentación relativa al token de acceso de usuario si aún no tiene ningún token disponible.
Agregue el código siguiente a la devolución de llamada onAppear
en ContentView.swift:
var userCredential: CommunicationTokenCredential?
do {
userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
print("ERROR: It was not possible to create user credential.")
return
}
self.callClient = CallClient()
// Creates the call agent
self.callClient?.createCallAgent(userCredential: userCredential!) { (agent, error) in
if error != nil {
print("ERROR: It was not possible to create a call agent.")
return
}
else {
self.callAgent = agent
print("Call agent successfully created.")
}
}
Inicio de una llamada
El método startCall
se establece como la acción que se llevará a cabo cuando se pulse el botón Iniciar llamada. Actualice la implementación para iniciar una llamada con ASACallAgent
:
func startCall()
{
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
// start call logic
let callees:[CommunicationIdentifier] = [MicrosoftTeamsAppIdentifier(self.callee)]
self.callAgent?.startCall(participants: callees, options: StartCallOptions()) { (call, error) in
if (error == nil) {
self.call = call
} else {
print("Failed to get call object")
}
}
}
}
}
También puede usar las propiedades de StartCallOptions
para establecer las opciones iniciales de la llamada (es decir, permite iniciar la llamada con el micrófono silenciado).
Finalización de una llamada
Implemente el método endCall
para finalizar la llamada actual cuando se toque el botón Finalizar llamada.
func endCall()
{
self.call!.hangUp(options: HangUpOptions()) { (error) in
if (error != nil) {
print("ERROR: It was not possible to hangup the call.")
}
}
}
Ejecución del código
Para compilar y ejecutar la aplicación en el simulador de iOS, seleccione Producto>Ejecutar o use el método abreviado de teclado (⌘-R).
Nota:
La primera vez que realice una llamada, el sistema le solicitará acceso al micrófono. En una aplicación de producción, debe usar la API AVAudioSession
para comprobar el estado del permiso y actualizar correctamente el comportamiento de la aplicación cuando no se conceda el permiso.
Pasos manuales para configurar la llamada:
- Inicio de la aplicación mediante Xcode
- Escriba el id. de objeto del operador automático (con prefijo) y seleccione el botón "Iniciar llamada". La aplicación iniciará la llamada saliente al operador automático con el id. de objeto especificado.
- La llamada está conectada al operador automático.
- El usuario de Communication Services se enruta a través del operador automático en función de su configuración.
Importante
Esta característica de Azure Communication Services se encuentra actualmente en versión preliminar.
Las API y los SDK en versión preliminar se proporcionan sin contrato de nivel de servicio. Se recomienda no usarlos para las cargas de trabajo de producción. Es posible que algunas características no sean compatibles o que sus funcionalidades estén limitadas.
Para obtener más información, consulte Términos de uso complementarios para las Versiones preliminares de Microsoft Azure.
En este inicio rápido, aprenderá a iniciar una llamada de un usuario de Azure Communication Services a un operador automático de Teams. Lo logrará con los pasos siguientes:
- Habilitar la federación de recurso de Azure Communication Services con el inquilino de Teams.
- Seleccione o cree el operador automático de Teams a través del Centro de Administración de Teams.
- Obtenga la dirección de correo electrónico del operador automático a través del Centro de Administración de Teams.
- Obtenga el id. de objeto del operador automático a través de Graph API.
- Iniciar una llamada con el SDK de llamadas de Azure Communication Services.
Si quiere ir directamente al final, puede descargar esta guía de inicio rápido como ejemplo desde GitHub.
Habilitar interoperabilidad en el inquilino de Teams
El usuario de Microsoft Entra con el rol de administrador de Teams puede ejecutar el cmdlet de PowerShell con el módulo de MicrosoftTeams para habilitar el recurso de Communication Services en el inquilino.
1. Preparar el módulo de Microsoft Teams
En primer lugar, abra PowerShell y valide la existencia del módulo de Teams con el siguiente comando:
Get-module *teams*
Si no ve el módulo MicrosoftTeams
, instálelo primero. Para instalar el módulo debe ejecutar PowerShell como administrador. Luego, ejecute el siguiente comando:
Install-Module -Name MicrosoftTeams
Se le informará sobre los módulos que se instalarán, que puede confirmar con una respuesta Y
o A
. Si el módulo está instalado pero no está actualizado, puede ejecutar el siguiente comando para actualizar el módulo:
Update-Module MicrosoftTeams
2. Conexión al módulo de Microsoft Teams
Cuando el módulo esté instalado y listo, puede conectarse al módulo MicrosoftTeams con el siguiente comando. Se le pedirá que inicie sesión con una ventana interactiva. La cuenta de usuario que va a usar debe tener permisos de administrador de Teams. De lo contrario, puede obtener una respuesta access denied
en los pasos siguientes.
Connect-MicrosoftTeams
3. Habilitar la configuración del inquilino
La interoperabilidad con los recursos de Communication Services se controla mediante la configuración de inquilinos y la directiva asignada. El inquilino de Teams tiene una configuración de inquilino único y los usuarios de Teams tienen asignada directiva global o directiva personalizada. Para más información, consulte Asignación de directivas en Teams.
Después de iniciar sesión correctamente, puede ejecutar el cmdlet Set-CsTeamsAcsFederationConfiguration para habilitar el recurso de Communication Services en el inquilino. Reemplace el texto IMMUTABLE_RESOURCE_ID
por un identificador de recurso inmutable en el recurso de comunicación. Puede encontrar más detalles sobre cómo obtener esta información aquí.
$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist
4. Habilitar una directiva de inquilino
A cada usuario de Teams se le ha asignado un External Access Policy
que determina si los usuarios de Communication Services pueden llamar a este usuario de Teams. Use el cmdlet Set-CsExternalAccessPolicy para asegurarse de que la directiva asignada al usuario de Teams ha establecido EnableAcsFederationAccess
en $true
Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true
Crear o seleccionar Operador automático de Teams
El operador automático de Teams es un sistema que proporciona un sistema de control de llamadas automatizado para las llamadas entrantes. Sirve de recepcionista virtual, permitiendo que los autores de llamadas se enruten automáticamente a la persona o departamento adecuados sin necesidad de un operador humano. Puede seleccionar un operador automático existente o crearlo a través del Centro de administración de Teams.
Obtenga más información sobre cómo crear un operador automático mediante el Centro de administración de Teams aquí.
Buscar el id. de objeto para el operador automático
Después de crear el operador automático, es necesario encontrar el id. de objeto correlacionado para usarlo más adelante para las llamadas. El id. de objeto está conectado a la cuenta de recursos que se adjuntó al operador automático: abra la pestaña Cuentas de recursos en Administración de Teams y busque el correo electrónico de la cuenta. Toda la información necesaria para la cuenta de recursos se puede encontrar en Explorador de Microsoft Graph utilizando este correo electrónico en la búsqueda.
https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com
En los resultados, encontraremos el campo "Id."
"userPrincipalName": "lab-test2-cq@contoso.com",
"id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"
Para usarlo en la aplicación que realiza la llamada, es necesario agregar un prefijo a este identificador. Actualmente se admite lo siguiente:
- Operador automático de nube pública:
28:orgid:<id>
- Operador automático de nube gubernamental:
28:gcch:<id>
Requisitos previos
Para completar este tutorial, debe cumplir los siguientes requisitos previos:
Una cuenta de Azure con una suscripción activa. Cree una cuenta gratuita.
Instale Visual Studio 2022 con la carga de trabajo de desarrollo de la Plataforma universal de Windows.
Un recurso de Communication Services implementado. Cree un recurso de Communication Services. Debe registrar la cadena de conexión para esta guía de inicio rápido.
Un token de acceso de usuario para su instancia de Azure Communication Services. También puede usar la CLI de Azure y ejecutar el comando siguiente con la cadena de conexión para crear un usuario y un token de acceso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Para más información, consulte Uso de la CLI de Azure para crear y administrar tokens de acceso.
Compatibilidad mínima con las aplicaciones de llamada de Teams: 1.10.0-beta.1
Instalación
Creación del proyecto
En Visual Studio, cree un proyecto con la plantilla Aplicación vacía (Windows universal) para configurar una aplicación para la Plataforma universal de Windows (UWP) de una sola página.
Instalar el paquete
Haga clic con el botón derecho en su proyecto y vaya a Manage Nuget Packages
para instalar la versión Azure.Communication.Calling.WindowsClient
1.4.0 o superior. Asegúrese de comprobar Include Prerelease
si desea ver las versiones de la versión preliminar pública.
Solicitar acceso
Vaya a Package.appxmanifest
y seleccione Capabilities
.
Compruebe Internet (Client)
y Internet (Client & Server)
para obtener acceso entrante y saliente a Internet. Compruebe Microphone
para acceder a la fuente de audio del micrófono y Webcam
para acceder a la fuente de vídeo de la cámara.
Instalación del marco de la aplicación
Es necesario configurar un diseño básico para adjuntar la lógica. Para hacer una llamada saliente, se necesita un elemento TextBox
para proporcionar el id. de usuario del destinatario. También se necesita un botón Start/Join call
y un botón Hang up
. También se incluyen en este ejemplo las casillas Mute
y BackgroundBlur
para mostrar las características de alternancia de estados de audio y efectos de vídeo.
Abra MainPage.xaml
del proyecto y agregue el nodo Grid
a Page
:
<Page
x:Class="CallingQuickstart.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CallingQuickstart"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="800" Height="600">
<!-- Don't forget to replace ‘CallingQuickstart’ with your project’s name -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="16*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="200*"/>
<RowDefinition Height="60*"/>
<RowDefinition Height="16*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="10,10,10,10" />
<Grid x:Name="AppTitleBar" Background="LightSeaGreen">
<TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="7,7,0,0"/>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
<MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
<StackPanel Orientation="Horizontal">
<Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
<CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
</StackPanel>
</StackPanel>
<TextBox Grid.Row="5" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
</Grid>
</Page>
Abra MainPage.xaml.cs
y reemplace el contenido por la siguiente implementación:
using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.Networking.PushNotifications;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace CallingQuickstart
{
public sealed partial class MainPage : Page
{
private const string authToken = "<AUTHENTICATION_TOKEN>";
private CallClient callClient;
private CallTokenRefreshOptions callTokenRefreshOptions = new CallTokenRefreshOptions(false);
private CallAgent callAgent;
private CommunicationCall call;
private LocalOutgoingAudioStream micStream;
#region Page initialization
public MainPage()
{
this.InitializeComponent();
// Additional UI customization code goes here
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
await InitCallAgentAndDeviceManagerAsync();
base.OnNavigatedTo(e);
}
#endregion
#region UI event handlers
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
// Start a call
}
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
// Hang up a call
}
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
// Toggle mute/unmute audio state of a call
}
#endregion
#region API event handlers
private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
{
// Handle incoming call event
}
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
// Handle connected and disconnected state change of a call
}
#endregion
#region Helper methods
private async Task InitCallAgentAndDeviceManagerAsync()
{
//Initialize the call agent and search for devices
}
private async Task<CommunicationCall> StartCallAsync(string acsCallee)
{
// Start a call to an Azure Communication Services user using the CallAgent and the callee id
}
#endregion
}
}
Modelo de objetos
La siguiente tabla enumera las clases y las interfaces controlan algunas de las características principales del SDK de llamadas de Azure Communication Services:
Nombre | Descripción |
---|---|
CallClient |
CallClient es el punto de entrada principal al SDK de llamada. |
CallAgent |
CallAgent se utiliza para iniciar y administrar las llamadas. |
CommunicationCall |
CommunicationCall se usa para administrar una llamada en curso. |
CallTokenCredential |
CallTokenCredential se usa como la credencial del token para crear una instancia de CallAgent . |
CallIdentifier |
CallIdentifier se usa para representar la identidad del usuario, que puede ser una de las opciones siguientes: UserCallIdentifier , PhoneNumberCallIdentifier , etc. |
Autenticar el cliente
Inicialice una instancia CallAgent
con un token de acceso de usuario que nos permitirá realizar y recibir llamadas y, opcionalmente, obtener una instancia de DeviceManager para consultar las configuraciones de dispositivo cliente.
En el código siguiente, reemplace <AUTHENTICATION_TOKEN>
por un token de acceso de usuario. Consulte la documentación relativa al token de acceso de usuario si aún no tiene ningún token disponible.
Agregue la función InitCallAgentAndDeviceManagerAsync
, que arranca el SDK. Este asistente se puede personalizar para cumplir los requisitos de la aplicación.
private async Task InitCallAgentAndDeviceManagerAsync()
{
this.callClient = new CallClient(new CallClientOptions() {
Diagnostics = new CallDiagnosticsOptions() {
// make sure to put your project AppName
AppName = "CallingQuickstart",
AppVersion="1.0",
Tags = new[] { "Calling", "ACS", "Windows" }
}
});
// Set up local audio stream using the first mic enumerated
var deviceManager = await this.callClient.GetDeviceManagerAsync();
var mic = deviceManager?.Microphones?.FirstOrDefault();
micStream = new LocalOutgoingAudioStream();
var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);
var callAgentOptions = new CallAgentOptions()
{
DisplayName = $"{Environment.MachineName}/{Environment.UserName}",
};
this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);
this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
}
Iniciar la llamada
Una vez obtenido un objeto StartCallOptions
, CallAgent
se puede usar para iniciar la llamada de Azure Communication Services:
private async Task<CommunicationCall> StartCallAsync(string acsCallee)
{
var options = new StartCallOptions();
var call = await this.callAgent.StartCallAsync( new [] { new MicrosoftTeamsAppCallIdentifier(acsCallee) }, options);
return call;
}
Finalizar una llamada
Finalice la llamada actual cuando se haga clic en el botón Hang up
. Agregue la implementación al HangupButton_Click para finalizar una llamada y detenga la vista previa y los flujos de vídeo.
private async void HangupButton_Click(object sender, RoutedEventArgs e)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
}
}
Alternar desactivar o activar audio
Desactive el audio saliente al hacer clic en el botón Mute
. Agregue la implementación al MuteLocal_Click para desactivar el audio de la llamada.
private async void MuteLocal_Click(object sender, RoutedEventArgs e)
{
var muteCheckbox = sender as CheckBox;
if (muteCheckbox != null)
{
var call = this.callAgent?.Calls?.FirstOrDefault();
if (call != null)
{
if ((bool)muteCheckbox.IsChecked)
{
await call.MuteOutgoingAudioAsync();
}
else
{
await call.UnmuteOutgoingAudioAsync();
}
}
// Update the UI to reflect the state
}
}
Aceptar una llamada entrante
El receptor de eventos IncomingCallReceived
se configura en el asistente de arranque del SDK InitCallAgentAndDeviceManagerAsync
.
this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
La aplicación tiene la oportunidad de configurar cómo se debe aceptar la llamada entrante, como los tipos de flujo de vídeo y audio.
private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
{
var incomingCall = args.IncomingCall;
var acceptCallOptions = new AcceptCallOptions() { };
call = await incomingCall.AcceptAsync(acceptCallOptions);
call.StateChanged += OnStateChangedAsync;
}
Supervisar y responder al evento de cambio de estado de llamada
El evento StateChanged
en el objeto CommunicationCall
se desencadena cuando una llamada en curso realiza transacciones de un estado a otro. La aplicación ofrece las oportunidades para reflejar los cambios de estado en la interfaz de usuario o insertar lógicas de negocios.
private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
{
var call = sender as CommunicationCall;
if (call != null)
{
var state = call.State;
// Update the UI
switch (state)
{
case CallState.Connected:
{
await call.StartAudioAsync(micStream);
break;
}
case CallState.Disconnected:
{
call.StateChanged -= OnStateChangedAsync;
call.Dispose();
break;
}
default: break;
}
}
}
Haga que el botón de llamada funcione
Una vez que Callee ID
no es null o está vacío, puede iniciar una llamada.
El estado de la llamada debe cambiarse mediante la acción OnStateChangedAsync
.
private async void CallButton_Click(object sender, RoutedEventArgs e)
{
var callString = CalleeTextBox.Text.Trim();
if (!string.IsNullOrEmpty(callString))
{
call = await StartCallAsync(callString);
call.StateChanged += OnStateChangedAsync;
}
}
Ejecución del código
Puede compilar y ejecutar el código en Visual Studio. Para las plataformas de la solución, se admiten ARM64
, x64
y x86
.
Pasos manuales para configurar la llamada:
- Inicie la aplicación mediante Visual Studio.
- Escriba el id. de objeto del operador automático (con prefijo) y seleccione el botón "Iniciar llamada". La aplicación iniciará la llamada saliente al operador automático con el id. de objeto especificado.
- La llamada está conectada al operador automático.
- El usuario de Communication Services se enruta a través del operador automático en función de su configuración.
Limpieza de recursos
Si quiere limpiar y quitar una suscripción a Communication Services, puede eliminar el recurso o grupo de recursos. Al eliminar el grupo de recursos, también se elimina cualquier otro recurso que esté asociado a él. Obtenga más información sobre la limpieza de recursos.
Pasos siguientes
Para más información, consulte los siguientes artículos.
- Introducción a las llamadas de interfaz de usuario a Aplicaciones de voz de Teams
- Más información sobre las Funcionalidades del SDK de llamadas
- Más información sobre cómo funciona la llamada