Partilhar via


Pedir acesso à câmara e ao microfone com Azure Communication Services Biblioteca de IU

Importante

Esta funcionalidade do Azure Communication Services está atualmente em pré-visualização.

As APIs de pré-visualização e os SDKs são fornecidos sem um contrato de nível de serviço. Recomendamos que não as utilize para cargas de trabalho de produção. Algumas funcionalidades podem não ser suportadas ou podem ter capacidades restritas.

Para obter mais informações, veja Termos de Utilização Suplementares para Pré-visualizações do Microsoft Azure.

Este tutorial é uma continuação de uma série de três partes de tutoriais de Preparação de Chamadas e segue-se a partir do anterior: Garantir que o utilizador está num browser suportado.

Transferir código

Aceda ao código completo deste tutorial no GitHub.

Pedir acesso à câmara e ao microfone

Para chamar aplicações, muitas vezes é vital que um utilizador tenha dado permissão para utilizar o microfone e a câmara. Nesta secção, criamos uma série de componentes que incentivam o utilizador a conceder acesso à câmara e ao microfone. Apresentamos pedidos ao utilizador para guiá-los através da concessão de acesso. Informamos o utilizador com um pedido se o acesso não for concedido.

A criar pedidos de acesso à câmara e ao microfone

Em primeiro lugar, criamos uma série de pedidos de permissões de dispositivos para colocar os utilizadores num estado em que aceitaram as permissões do microfone e da câmara. Estes pedidos utilizam o CameraAndMicrophoneSitePermissions componente da Biblioteca de IU. Tal como a linha de comandos Browser Não Suportada, alojamos estes pedidos dentro de um 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>
);

Verificar o acesso à câmara e ao microfone

Aqui, adicionamos duas novas funções utilitárias para verificar e pedir acesso à câmara e ao microfone. Crie um ficheiro chamado devicePermissionUtils.ts com duas funções checkDevicePermissionsState e requestCameraAndMicrophonePermissions. checkDevicePermissionsState utiliza a PermissionAPI. No entanto, a consulta da câmara e do microfone não é suportada no Firefox, pelo que garantimos que este método é devolvido unknown neste caso. Mais tarde, asseguramos que processamos o unknown caso quando solicitamos permissões ao utilizador.

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
}

Pedir ao utilizador para conceder acesso à câmara e ao microfone

Agora que temos os pedidos e verificamos e solicitamos lógica, criamos um DeviceAccessComponent para pedir ao utilizador sobre as permissões do dispositivo. Neste componente, apresentamos diferentes pedidos ao utilizador com base no estado de permissão do dispositivo:

  • Se o estado de permissão do dispositivo for desconhecido, apresentamos um pedido ao utilizador a informá-lo de que estamos a verificar as permissões do dispositivo.
  • Se estivermos a pedir permissões, apresentamos um pedido ao utilizador a incentivá-lo a aceitar o pedido de permissões.
  • Se as permissões forem negadas, apresentamos um pedido ao utilizador a informá-lo de que negou as permissões e de que tem de conceder permissões 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'} />
    </>
  );
}

Depois de concluirmos a criação deste componente, adicionamo-lo ao App.tsx. Primeiro, adicione a importação:

import { DeviceAccessChecksComponent } from './DeviceAccessChecksComponent';

Em seguida, atualize o TestingState tipo para ser o seguinte valor:

type TestingState = 'runningEnvironmentChecks' | 'runningDeviceAccessChecks' | 'finished';

Por fim, atualize o App componente:

/**
 * 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>
  );
}

A aplicação apresenta ao utilizador pedidos para guiá-lo através do acesso ao dispositivo:

Gif a mostrar o utilizador a pedir acesso à câmara e ao microfone

Nota

Para testes, recomendamos que visite a sua aplicação no modo InPrivate/Incógnito para que as permissões da câmara e do microfone não tenham sido concedidas anteriormente para localhost:3000.

Passos seguintes