Files
CamTalk/frontend/src/components/MicManager/index.tsx
cfy666 6967dd7b2e feat: 实现摄像头和麦克风设备选择功能
- 新增 useDeviceList hook,枚举音视频输入设备并监听热插拔
- CameraManager/MicManager 的 startCamera/startMic 支持可选 deviceId 参数
- useVisionSession 集成设备选择:授权后自动枚举、切换设备时热重启
- 连接后显示设备下拉选择器,未连接时隐藏(避免未授权时空列表)
- SessionConfig 新增 cameraDeviceId/micDeviceId 持久化到 localStorage
2026-06-21 16:49:48 +08:00

52 lines
1.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// MicManager — 麦克风音频采集
// 职责:获取麦克风 MediaStream供 VAD 和音频录制使用
// ============================================================
import { useCallback, useRef, useState } from "react";
import { useI18n } from "../../lib/i18n";
export function useMicrophone() {
const { t } = useI18n();
const [stream, setStream] = useState<MediaStream | null>(null);
const [error, setError] = useState<string | null>(null);
const audioContextRef = useRef<AudioContext | null>(null);
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: audioConstraints,
video: false,
});
setStream(mediaStream);
setError(null);
return mediaStream;
} catch (err) {
const message = err instanceof Error ? err.message : t("error.micAccess");
setError(message);
console.error("[Mic] 获取麦克风失败:", err);
return null;
}
}, []);
const stopMic = useCallback(() => {
stream?.getTracks().forEach((track) => track.stop());
setStream(null);
audioContextRef.current?.close();
audioContextRef.current = null;
}, [stream]);
/** 获取或创建 AudioContext */
const getAudioContext = useCallback((): AudioContext => {
if (!audioContextRef.current) {
audioContextRef.current = new AudioContext({ sampleRate: 16000 });
}
return audioContextRef.current;
}, []);
return { stream, error, startMic, stopMic, getAudioContext };
}