Поделиться через


Запрос доступа к камере и микрофону с помощью библиотеки пользовательского интерфейса Службы коммуникации Azure

Важно!

Эта функция Службы коммуникации Azure в настоящее время находится на этапе предварительной версии.

Предварительные версии API и пакеты SDK предоставляются без соглашения об уровне обслуживания. Не рекомендуется использовать их для рабочих нагрузок. Некоторые функции могут не поддерживаться или иметь ограниченные возможности.

Дополнительные сведения см. в статье Дополнительные условия использования предварительных версий Microsoft Azure.

Это руководство является продолжением серии из трех частей учебников по готовности к вызову и следует из предыдущего: Убедитесь, что пользователь находится в поддерживаемом браузере.

Скачать код

Полный код этого руководства можно получить на сайте GitHub.

Запрос доступа к камере и микрофону

Для вызова приложений часто важно, чтобы пользователь предоставил разрешение на использование микрофона и камеры. В этом разделе мы создадим ряд компонентов, которые побуждают пользователя предоставить доступ к камере и микрофону. Мы отобразим пользователю запросы на предоставление доступа. Если доступ не предоставлен, мы сообщаем пользователю с запросом.

Создание запросов на доступ к камере и микрофону

Сначала мы создадим серию запросов на разрешения устройства, чтобы пользователи переходили в состояние, в котором они приняли разрешения микрофона и камеры. Эти запросы используют CameraAndMicrophoneSitePermissions компонент из библиотеки пользовательского интерфейса. Как и в командной строке неподдерживаемого браузера, эти запросы размещаются в 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>
);

Проверка доступа к камере и микрофону

Здесь мы добавим две новые служебные функции для проверки и запроса доступа к камере и микрофону. Создайте файл с именем devicePermissionUtils.ts с двумя функциями checkDevicePermissionsState и requestCameraAndMicrophonePermissions. checkDevicePermissionsState использует PermissionAPI. Однако запросы камеры и микрофона не поддерживаются в Firefox, поэтому в этом случае мы гарантируем, что этот метод возвращает результат unknown . Позже мы убедимся, что мы обработаем этот unknown случай при запросе у пользователя разрешений.

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
}

Запрос на предоставление пользователю доступа к камере и микрофону

Теперь у нас есть запросы и логика проверки и запроса, мы создадим DeviceAccessComponent , чтобы запросить у пользователя разрешения устройства. В этом компоненте отображаются различные запросы для пользователя в зависимости от состояния разрешения устройства:

  • Если состояние разрешений устройства неизвестно, пользователю отображается сообщение о проверке разрешений устройства.
  • Если мы запрашиваем разрешения, мы отобразим пользователю запрос на принятие разрешений.
  • Если в разрешениях отказано, мы отобразим пользователю сообщение о том, что он отказал в разрешениях и что ему нужно предоставить разрешения, чтобы продолжить.

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

Завершив создание этого компонента, мы добавим его в App.tsx. Сначала добавьте импорт:

import { DeviceAccessChecksComponent } from './DeviceAccessChecksComponent';

Затем измените TestingState тип на следующее значение:

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

Наконец, обновите 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>
  );
}

Приложение предоставляет пользователю запросы, которые помогут ему получить доступ к устройству:

GIF- изображение, показывающее, как пользователю предлагается доступ к камере и микрофону

Примечание

Для тестирования рекомендуется посетить приложение в режиме InPrivate/инкогнито, чтобы разрешения камеры и микрофона не были предоставлены ранее для localhost:3000.

Дальнейшие действия