Solicitar acceso a la cámara y al micrófono mediante la biblioteca de interfaz de usuario de Azure Communication Services
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.
Este tutorial es una continuación de una serie de tres partes de tutoriales de preparación para llamadas y sigue al anterior: Garantía de que el usuario utiliza en un explorador compatible.
Descarga de código
Acceda al código completo de este tutorial sobre GitHub.
Solicitud de acceso a la cámara y al micrófono
Para las aplicaciones de llamadas, a menudo es fundamental que un usuario haya concedido permiso para usar el micrófono y la cámara. En esta sección, se crea una serie de componentes que animan al usuario a conceder acceso a la cámara y al micrófono. Se muestran avisos al usuario para guiarle por la concesión de acceso. Se notifica al usuario con un mensaje si el acceso no se concede.
Creación de avisos para el acceso a la cámara y al micrófono
En primer lugar, se crean una serie de solicitudes de permisos de dispositivo para que los usuarios entren en un estado en el que han aceptado los permisos del micrófono y la cámara. En estos avisos se usa el componente CameraAndMicrophoneSitePermissions
de la biblioteca de interfaz de usuario. Al igual que el aviso de explorador no admitido, estos mensajes se hospedan dentro de FluentUI modal
.
src/DevicePermissionPrompts.tsx
import { CameraAndMicrophoneSitePermissions } from '@azure/communication-react';
import { Modal } from '@fluentui/react';
/** Modal dialog that prompt the user to accept the Browser's device permission request. */
export const AcceptDevicePermissionRequestPrompt = (props: { isOpen: boolean }): JSX.Element => (
<PermissionsModal isOpen={props.isOpen} kind="request" />
);
/** Modal dialog that informs the user we are checking for device access. */
export const CheckingDeviceAccessPrompt = (props: { isOpen: boolean }): JSX.Element => (
<PermissionsModal isOpen={props.isOpen} kind="check" />
)
/** Modal dialog that informs the user they denied permission to the camera or microphone with corrective steps. */
export const PermissionsDeniedPrompt = (props: { isOpen: boolean }): JSX.Element => (
<PermissionsModal isOpen={props.isOpen} kind="denied" />
);
/** Base component utilized by the above prompts for better code separation. */
const PermissionsModal = (props: { isOpen: boolean, kind: "denied" | "request" | "check" }): JSX.Element => (
<Modal isOpen={props.isOpen}>
<CameraAndMicrophoneSitePermissions
appName={'this site'}
kind={props.kind}
onTroubleshootingClick={() => alert('This callback should be used to take the user to further troubleshooting')}
/>
</Modal>
);
Comprobación del acceso a la cámara y al micrófono
Aquí se agregan dos nuevas funciones de utilidad para comprobar y solicitar acceso a la cámara y al micrófono. Cree un archivo llamado devicePermissionUtils.ts
con dos funciones: checkDevicePermissionsState
y requestCameraAndMicrophonePermissions
.
En checkDevicePermissionsState
se usa PermissionAPI. Pero en Firefox no se admite la consulta de la cámara y el micrófono; por tanto, se garantiza que este método devuelva unknown
en este caso. Más adelante, se asegurará de controlar el caso unknown
al solicitar permisos al usuario.
src/DevicePermissionUtils.ts
import { DeviceAccess } from "@azure/communication-calling";
import { StatefulCallClient } from "@azure/communication-react";
/**
* Check if the user needs to be prompted for camera and microphone permissions.
*
* @remarks
* The Permissions API we are using is not supported in Firefox, Android WebView or Safari < 16.
* In those cases this returns 'unknown'.
*/
export const checkDevicePermissionsState = async (): Promise<{camera: PermissionState, microphone: PermissionState} | 'unknown'> => {
try {
const [micPermissions, cameraPermissions] = await Promise.all([
navigator.permissions.query({ name: "microphone" as PermissionName }),
navigator.permissions.query({ name: "camera" as PermissionName })
]);
console.info('PermissionAPI results', [micPermissions, cameraPermissions]); // view console logs in the browser to see what the PermissionsAPI info is returned
return { camera: cameraPermissions.state, microphone: micPermissions.state };
} catch (e) {
console.warn("Permissions API unsupported", e);
return 'unknown';
}
}
/** Use the DeviceManager to request for permissions to access the camera and microphone. */
export const requestCameraAndMicrophonePermissions = async (callClient: StatefulCallClient): Promise<DeviceAccess> => {
const response = await (await callClient.getDeviceManager()).askDevicePermission({ audio: true, video: true });
console.info('AskDevicePermission response', response); // view console logs in the browser to see what device access info is returned
return response
}
Solicitud al usuario de que conceda acceso a la cámara y al micrófono
Ahora que ya tiene los avisos y la lógica de comprobación y solicitud, se crea un elemento DeviceAccessComponent
para solicitar al usuario los permisos del dispositivo.
En este componente, se muestran diferentes mensajes al usuario en función del estado de permiso del dispositivo:
- Si el estado del permiso del dispositivo es desconocido, se muestra un mensaje al usuario que le informa de que se están comprobando los permisos del dispositivo.
- Si se solicitan permisos, se muestra un mensaje al usuario que le anima a aceptar la solicitud de permisos.
- Si los permisos se deniegan, se muestra un mensaje al usuario que le informa de que ha denegado los permisos y que necesita concederlos para continuar.
src/DeviceAccessChecksComponent.tsx
import { useEffect, useState } from 'react';
import { CheckingDeviceAccessPrompt, PermissionsDeniedPrompt, AcceptDevicePermissionRequestPrompt } from './DevicePermissionPrompts';
import { useCallClient } from '@azure/communication-react';
import { checkDevicePermissionsState, requestCameraAndMicrophonePermissions } from './DevicePermissionUtils';
export type DevicesAccessChecksState = 'runningDeviceAccessChecks' |
'checkingDeviceAccess' |
'promptingForDeviceAccess' |
'deniedDeviceAccess';
/**
* This component is a demo of how to use the StatefulCallClient with CallReadiness Components to get a user
* ready to join a call.
* This component checks the browser support and if camera and microphone permissions have been granted.
*/
export const DeviceAccessChecksComponent = (props: {
/**
* Callback triggered when the tests are complete and successful
*/
onTestsSuccessful: () => void
}): JSX.Element => {
const [currentCheckState, setCurrentCheckState] = useState<DevicesAccessChecksState>('runningDeviceAccessChecks');
// Run call readiness checks when component mounts
const callClient = useCallClient();
useEffect(() => {
const runDeviceAccessChecks = async (): Promise<void> => {
// First we check if we need to prompt the user for camera and microphone permissions.
// The prompt check only works if the browser supports the PermissionAPI for querying camera and microphone.
// In the event that is not supported, we show a more generic prompt to the user.
const devicePermissionState = await checkDevicePermissionsState();
if (devicePermissionState === 'unknown') {
// We don't know if we need to request camera and microphone permissions, so we'll show a generic prompt.
setCurrentCheckState('checkingDeviceAccess');
} else if (devicePermissionState.camera === 'prompt' || devicePermissionState.microphone === 'prompt') {
// We know we need to request camera and microphone permissions, so we'll show the prompt.
setCurrentCheckState('promptingForDeviceAccess');
}
// Now the user has an appropriate prompt, we can request camera and microphone permissions.
const devicePermissionsState = await requestCameraAndMicrophonePermissions(callClient);
if (!devicePermissionsState.audio || !devicePermissionsState.video) {
// If the user denied camera and microphone permissions, we prompt the user to take corrective action.
setCurrentCheckState('deniedDeviceAccess');
} else {
// Test finished successfully, trigger callback to parent component to take user to the next stage of the app.
props.onTestsSuccessful();
}
};
runDeviceAccessChecks();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
{/* We show this when we are prompting the user to accept device permissions */}
<AcceptDevicePermissionRequestPrompt isOpen={currentCheckState === 'promptingForDeviceAccess'} />
{/* We show this when the PermissionsAPI is not supported and we are checking what permissions the user has granted or denied */}
<CheckingDeviceAccessPrompt isOpen={currentCheckState === 'checkingDeviceAccess'} />
{/* We show this when the user has failed to grant camera and microphone access */}
<PermissionsDeniedPrompt isOpen={currentCheckState === 'deniedDeviceAccess'} />
</>
);
}
Después de terminar de crear este componente, se agrega a App.tsx
. En primer lugar, se agrega la importación:
import { DeviceAccessChecksComponent } from './DeviceAccessChecksComponent';
Después, se actualiza el tipo TestingState
para que sea el siguiente valor:
type TestingState = 'runningEnvironmentChecks' | 'runningDeviceAccessChecks' | 'finished';
Por último, se actualiza el componente App
:
/**
* Entry point of a React app.
*
* This shows a PreparingYourSession component while the CallReadinessChecks are running.
* Once the CallReadinessChecks are finished, the TestComplete component is shown.
*/
const App = (): JSX.Element => {
const [testState, setTestState] = useState<TestingState>('runningEnvironmentChecks');
return (
<FluentThemeProvider>
<CallClientProvider callClient={callClient}>
{/* Show a Preparing your session screen while running the environment checks */}
{testState === 'runningEnvironmentChecks' && (
<>
<PreparingYourSession />
<EnvironmentChecksComponent onTestsSuccessful={() => setTestState('runningDeviceAccessChecks')} />
</>
)}
{/* Show a Preparing your session screen while running the device access checks */}
{testState === 'runningDeviceAccessChecks' && (
<>
<PreparingYourSession />
<DeviceAccessChecksComponent onTestsSuccessful={() => setTestState('finished')} />
</>
)}
{/* After the device setup is complete, take the user to the call. For this sample we show a test complete page. */}
{testState === 'finished' && <TestComplete />}
</CallClientProvider>
</FluentThemeProvider>
);
}
La aplicación presenta al usuario mensajes para guiarle por el acceso al dispositivo:
Nota
Para realizar pruebas, se recomienda visitar la aplicación en modo InPrivate/Incógnito para que no se hayan concedido previamente permisos de cámara y micrófono para localhost:3000
.