feat: 实现摄像头和麦克风设备选择功能

- 新增 useDeviceList hook,枚举音视频输入设备并监听热插拔
- CameraManager/MicManager 的 startCamera/startMic 支持可选 deviceId 参数
- useVisionSession 集成设备选择:授权后自动枚举、切换设备时热重启
- 连接后显示设备下拉选择器,未连接时隐藏(避免未授权时空列表)
- SessionConfig 新增 cameraDeviceId/micDeviceId 持久化到 localStorage
This commit is contained in:
2026-06-21 16:49:48 +08:00
parent eea5c07eaa
commit 6967dd7b2e
9 changed files with 128 additions and 37 deletions

View File

@@ -19,10 +19,13 @@ export function useCamera() {
const [stream, setStream] = useState<MediaStream | null>(null);
const [error, setError] = useState<string | null>(null);
const startCamera = useCallback(async () => {
const startCamera = useCallback(async (deviceId?: string) => {
try {
const videoConstraints: MediaTrackConstraints = deviceId
? { deviceId: { exact: deviceId }, width: 640, height: 480 }
: { facingMode: "environment", width: 640, height: 480 };
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: "environment", width: 640, height: 480 },
video: videoConstraints,
audio: false,
});
setStream(mediaStream);

View File

@@ -12,15 +12,13 @@ export function useMicrophone() {
const [error, setError] = useState<string | null>(null);
const audioContextRef = useRef<AudioContext | null>(null);
const startMic = useCallback(async () => {
const startMic = useCallback(async (deviceId?: string) => {
try {
const audioConstraints: MediaTrackConstraints = deviceId
? { deviceId: { exact: deviceId }, sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true }
: { sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true };
const mediaStream = await navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 16000,
channelCount: 1,
echoCancellation: true,
noiseSuppression: true,
},
audio: audioConstraints,
video: false,
});
setStream(mediaStream);