设备检测(Web)

最近更新时间:2025-12-29 17:09:52

我的收藏
本篇文档将指导您如何在用户加入房间前提供设备预览与测试功能。通过 Atomicx 提供的进房前测试接口,您可以引导用户检查并调整音视频设备状态,确保以最佳状态进入多人音视频房间。

功能介绍

设备预览是保障音视频房间顺利运行的重要环节。Atomicx 专为进房前场景提供了一套独立的测试接口,其核心能力包括:
视频画面预览:在不推流的情况下启动摄像头,验证画面清晰度与光线。
麦克风检测:通过实时音量反馈,确认麦克风采集是否正常。
扬声器校准:通过播放测试音,确认输出设备及其音量设置。

前提条件

用户已经通过 useLoginState 完成登录鉴权,请参考 接入概览

实现设备检测功能

步骤1:摄像头测试

使用 startCameraTest 方法启动摄像头测试。该方法仅用于本地预览,不会产生网络流量。
代码示例:
<template>
<div class="preview-container">
<!-- 摄像头预览区域 -->
<div id="camera-preview" class="camera-preview">
<!-- 加载状态 -->
<div v-if="isCameraTestLoading" class="loading-overlay">
<div class="loading-spinner" />
<span>正在启动摄像头...</span>
</div>
</div>

<!-- 摄像头选择 -->
<div class="camera-selector">
<label>选择摄像头</label>
<select
:value="currentCamera?.deviceId"
@change="handleCameraChange"
:disabled="isCameraTestLoading"
>
<option
v-for="camera in cameraList"
:key="camera.deviceId"
:value="camera.deviceId"
>
{{ camera.deviceName }}
</option>
</select>
</div>

<!-- 控制按钮 -->
<div class="control-buttons">
<button @click="getCameraList">获取摄像头列表</button>
<button
@click="toggleCameraTest"
:disabled="isCameraTestLoading"
>
{{ isCameraTesting ? '停止预览' : '开始预览' }}
</button>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { IconWarning } from '@tencentcloud/uikit-base-component-vue3';
import {
useLoginState,
useDeviceState,
DeviceStatus,
DeviceError,
} from 'tuikit-atomicx-vue3/room';

const {
isCameraTesting,
isCameraTestLoading,
cameraList,
currentCamera,
startCameraTest,
stopCameraTest,
getCameraList,
setCurrentCamera
} = useDeviceState();

onUnmounted(() => {
if (isCameraTesting.value) {
stopCameraTest();
}
});

const toggleCameraTest = async () => {
if (isCameraTesting.value) {
await stopCameraTest();
} else {
const previewElement = document.getElementById('camera-preview');
if (previewElement) {
await startCameraTest({ view: previewElement });
}
}
};

const handleCameraChange = async (event: Event) => {
const deviceId = (event.target as HTMLSelectElement).value;
try {
await setCurrentCamera({ deviceId });
} catch (error) {
console.error('切换摄像头失败:', error);
}
};
</script>

<style scoped>
.preview-container { position: relative; width: 100%; max-width: 640px; margin: 0 auto;}.camera-preview { width: 100%; position: relative; aspect-ratio: 16 / 9; background-color: #000; border-radius: 8px; overflow: hidden;}.loading-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; background-color: rgba(0, 0, 0, 0.7); color: white; border-radius: 8px;}.loading-spinner { width: 40px; height: 40px; border: 4px solid rgba(255, 255, 255, 0.3); border-top-color: white; border-radius: 50%; animation: spin 0.8s linear infinite;}@keyframes spin { to { transform: rotate(360deg); }}.error-message { display: flex; align-items: center; gap: 8px; padding: 12px; margin-top: 12px; background-color: #ffebee; color: #c62828; border-radius: 4px; font-size: 14px;}.camera-selector { margin-top: 16px; display: flex; align-items: center; gap: 12px;}.camera-selector label { font-weight: 500; min-width: 80px;}.camera-selector select { flex: 1; padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px;}.control-buttons { margin-top: 16px; display: flex; gap: 12px;}.control-buttons button { flex: 1; padding: 12px 24px; background-color: #1976d2; color: white; border: none; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.3s;}.control-buttons button:hover:not(:disabled) { background-color: #1565c0;}.control-buttons button:disabled { background-color: #bdbdbd; cursor: not-allowed;}
</style>

步骤2:麦克风测试

通过 startMicrophoneTest 启动麦克风测试,并利用 testingMicVolume 实现 UI 音量条的实时波动。
代码示例:
<template>
<div class="microphone-test">
<!-- 音量显示 -->
<div class="volume-display">
<div class="volume-label">麦克风音量</div>
<div class="volume-bar-container">
<div
class="volume-bar"
:style="{ width: `${testingMicVolume}%` }"
:class="volumeClass"
/>
</div>
<div class="volume-value">{{ testingMicVolume }}</div>
</div>

<!-- 麦克风选择 -->
<div class="microphone-selector">
<label>选择麦克风</label>
<select
:value="currentMicrophone?.deviceId"
@change="handleMicrophoneChange"
:disabled="isMicrophoneTesting"
>
<option
v-for="mic in microphoneList"
:key="mic.deviceId"
:value="mic.deviceId"
>
{{ mic.deviceName }}
</option>
</select>
</div>

<!-- 控制按钮 -->
<div class="control-buttons">
<button
@click="getMicrophoneList"
>
获取设备
</button>
<button
@click="toggleMicrophoneTest"
>
{{ isMicrophoneTesting ? '停止测试' : '开始测试' }}
</button>
</div>

<!-- 测试提示 -->
<div v-if="isMicrophoneTesting" class="test-tip">
<span>请对着麦克风说话,查看音量变化</span>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import {
useDeviceState,
DeviceError,
} from 'tuikit-atomicx-vue3/room';

const {
isMicrophoneTesting,
testingMicVolume,
microphoneList,
currentMicrophone,
startMicrophoneTest,
stopMicrophoneTest,
getMicrophoneList,
setCurrentMicrophone
} = useDeviceState();

// 音量条样式类
const volumeClass = computed(() => {
const volume = testingMicVolume.value;
if (volume === 0) return 'volume-zero';
if (volume < 30) return 'volume-low';
if (volume < 70) return 'volume-medium';
return 'volume-high';
});

// 清理:停止测试
onUnmounted(() => {
if (isMicrophoneTesting.value) {
stopMicrophoneTest();
}
});

// 切换麦克风测试
const toggleMicrophoneTest = async () => {
if (isMicrophoneTesting.value) {
await stopMicrophoneTest();
} else {
try {
await startMicrophoneTest({ interval: 200 });
} catch (error) {
console.error('启动麦克风测试失败:', error);
}
}
};

// 切换麦克风设备
const handleMicrophoneChange = async (event: Event) => {
const deviceId = (event.target as HTMLSelectElement).value;
try {
await setCurrentMicrophone({ deviceId });
} catch (error) {
console.error('切换麦克风失败:', error);
}
};
</script>

<style scoped>
.microphone-test { width: 100%; max-width: 640px; margin: 0 auto;}.volume-display { padding: 24px; background-color: #f5f5f5; border-radius: 8px; margin-bottom: 16px;}.volume-label { font-size: 14px; font-weight: 500; color: #666; margin-bottom: 12px;}.volume-bar-container { width: 100%; height: 24px; background-color: #e0e0e0; border-radius: 12px; overflow: hidden; margin-bottom: 8px;}.volume-bar { height: 100%; transition: width 0.1s ease; border-radius: 12px;}.volume-bar.volume-zero { background-color: #9e9e9e;}.volume-bar.volume-low { background-color: #ff9800;}.volume-bar.volume-medium { background-color: #4caf50;}.volume-bar.volume-high { background-color: #2196f3;}.volume-value { text-align: center; font-size: 18px; font-weight: 600; color: #333;}.microphone-selector { display: flex; align-items: center; gap: 12px; margin-bottom: 16px;}.microphone-selector label { font-weight: 500; min-width: 80px;}.microphone-selector select { flex: 1; padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px;}.error-message { display: flex; align-items: center; gap: 8px; padding: 12px; margin-bottom: 16px; background-color: #ffebee; color: #c62828; border-radius: 4px; font-size: 14px;}.control-buttons { margin-bottom: 12px; display: flex; gap: 8px; }.control-buttons button { flex: 1; width: 100%; padding: 12px 24px; background-color: #1976d2; color: white; border: none; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.3s;}.control-buttons button:hover:not(:disabled) { background-color: #1565c0;}.control-buttons button:disabled { background-color: #bdbdbd; cursor: not-allowed;}.test-tip { display: flex; align-items: center; gap: 8px; padding: 12px; background-color: #e3f2fd; color: #1565c0; border-radius: 4px; font-size: 13px;}
</style>

步骤3:扬声器测试

使用 startSpeakerTest 方法播放一段指定的音频文件来确认扬声器是否工作正常。
代码示例:
<template>
<div class="speaker-test">
<!-- 扬声器选择 -->
<div class="speaker-selector">
<label>选择扬声器</label>
<select
:value="currentSpeaker?.deviceId"
@change="handleSpeakerChange"
:disabled="isSpeakerTesting"
>
<option
v-for="speaker in speakerList"
:key="speaker.deviceId"
:value="speaker.deviceId"
>
{{ speaker.deviceName }}
</option>
</select>
</div>

<!-- 控制按钮 -->
<div class="control-buttons">
<button @click="getSpeakerList">获取扬声器列表</button>
<button
@click="toggleSpeakerTest"
:disabled="isSpeakerTesting"
>
{{ isSpeakerTesting ? '正在播放...' : '播放测试音频' }}
</button>
<button
v-if="isSpeakerTesting"
@click="stopSpeakerTest"
>
停止播放
</button>
</div>

<!-- 测试提示 -->
<div v-if="isSpeakerTesting" class="test-tip">
<span>正在播放测试音频,请确认是否能听到声音</span>
</div>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { useDeviceState } from 'tuikit-atomicx-vue3/room';

const {
isSpeakerTesting,
speakerList,
currentSpeaker,
startSpeakerTest,
stopSpeakerTest,
getSpeakerList,
setCurrentSpeaker,
} = useDeviceState();

// 测试音频文件路径
// 注意:此 URL 仅供开发测试使用,生产环境请使用您自己的音频文件,建议使用 3-5 秒的 MP3 格式音频文件
const testAudioPath = 'https://web.sdk.qcloud.com/trtc/electron/download/resources/media/TestSpeaker.mp3';

// 清理:停止测试
onUnmounted(() => {
if (isSpeakerTesting.value) {
stopSpeakerTest();
}
});

// 切换扬声器测试
const toggleSpeakerTest = async () => {
if (isSpeakerTesting.value) {
await stopSpeakerTest();
} else {
try {
await startSpeakerTest({ filePath: testAudioPath });
} catch (error) {
console.error('启动扬声器测试失败:', error);
}
}
};

// 切换扬声器设备
const handleSpeakerChange = async (event: Event) => {
const deviceId = (event.target as HTMLSelectElement).value;
try {
await setCurrentSpeaker({ deviceId });
} catch (error) {
console.error('切换扬声器失败:', error);
}
};
</script>

<style scoped>
.speaker-test { width: 100%; max-width: 640px; margin: 0 auto;}.speaker-selector { display: flex; align-items: center; gap: 12px; margin-bottom: 24px;}.speaker-selector label { font-weight: 500; min-width: 80px;}.speaker-selector select { flex: 1; padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 14px;}.volume-slider-container { display: flex; align-items: center; gap: 12px;}.volume-slider { flex: 1; height: 6px; border-radius: 3px; background: #e0e0e0; outline: none; -webkit-appearance: none;}.volume-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: #1976d2; cursor: pointer;}.volume-slider::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: #1976d2; cursor: pointer; border: none;}.volume-value { min-width: 50px; text-align: right; font-weight: 500; color: #333;}.control-buttons { display: flex; gap: 12px; margin-bottom: 12px;}button { flex: 1; padding: 12px 24px; background-color: #1976d2; color: white; border: none; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 0.3s;}.control-buttons button:hover:not(:disabled) { background-color: #1565c0;}.control-buttons button:disabled { background-color: #bdbdbd; cursor: not-allowed;}.test-tip { display: flex; align-items: center; gap: 8px; padding: 12px; background-color: #e3f2fd; color: #1565c0; border-radius: 4px; font-size: 13px;}
</style>

API 文档

State/Component
功能描述
API 文档
useDeviceState
包含音视频设备状态,音视频设备列表及操作接口。

常见问题

为什么摄像头预览没有画面?

可能的原因包括:
权限被拒绝:检查浏览器是否允许摄像头权限。
设备被占用:关闭其他使用摄像头的应用。
设备未连接:检查摄像头是否正确连接。
浏览器不支持:使用 Chrome、Edge 等现代浏览器。

麦克风测试时音量一直为 0?

可能的原因包括:
麦克风权限未授予:检查浏览器权限设置。
麦克风被静音:检查系统麦克风设置。
设备选择错误:切换到正确的麦克风设备。