你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
使用 Azure 通信服务 UI 库请求相机和麦克风访问
重要
Azure 通信服务的这一功能目前以预览版提供。
预览版 API 和 SDK 在没有服务级别协议的情况下提供。 建议不要将它们用于生产工作负荷。 某些功能可能不受支持或者已受限。
有关详细信息,请参阅 Microsoft Azure 预览版补充使用条款。
本教程是“呼叫准备”三部分系列教程的续篇,承自上一篇:确保用户使用受支持的浏览器。
下载代码
在 GitHub 上访问本教程的完整代码。
请求访问相机和麦克风
对于呼叫应用程序,用户授予使用麦克风和相机的权限通常至关重要。 在本部分中,我们将创建一系列组件,用于支持用户授予对相机和麦克风的访问权限。 我们向用户显示提示,以指导他们授予访问权限。 如果未授予访问权限,我们会通过提示通知用户。
创建相机和麦克风访问提示
我们首先创建一系列设备权限提示,让用户进入他们已接受麦克风和相机权限的状态。 这些提示使用 UI 库中的 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 语句:
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>
);
}
应用向用户显示提示,引导他们完成设备访问:
注意
对于测试,我们建议在 InPrivate/Incognito 模式下访问应用,在这之前就不需要为 localhost:3000
授予相机和麦克风权限。