feat: 实现摄像头和麦克风设备选择功能
- 新增 useDeviceList hook,枚举音视频输入设备并监听热插拔 - CameraManager/MicManager 的 startCamera/startMic 支持可选 deviceId 参数 - useVisionSession 集成设备选择:授权后自动枚举、切换设备时热重启 - 连接后显示设备下拉选择器,未连接时隐藏(避免未授权时空列表) - SessionConfig 新增 cameraDeviceId/micDeviceId 持久化到 localStorage
This commit is contained in:
@@ -96,6 +96,9 @@ function AppContent() {
|
||||
toggleCamera,
|
||||
toggleMic,
|
||||
sendTextMessage,
|
||||
cameras,
|
||||
mics,
|
||||
switchDevice,
|
||||
} = useVisionSession(accessToken, activeSessionId);
|
||||
|
||||
const isConnected = connectionStatus === "connected";
|
||||
@@ -406,21 +409,6 @@ function AppContent() {
|
||||
<button className="btn btn--primary btn--lg" onClick={startSession}>
|
||||
{connectionStatus === "connecting" ? tr("controls.connecting") : tr("controls.startVideo")}
|
||||
</button>
|
||||
{/* 设备选择器 */}
|
||||
<div className="video-controls__devices">
|
||||
<div className="device-select-wrapper">
|
||||
<label className="device-select-label">📷 {tr("controls.device.camera")}</label>
|
||||
<select className="device-select" defaultValue="default">
|
||||
<option value="default">{tr("controls.device.default")}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="device-select-wrapper">
|
||||
<label className="device-select-label">🎤 {tr("controls.device.mic")}</label>
|
||||
<select className="device-select" defaultValue="default">
|
||||
<option value="default">{tr("controls.device.default")}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : isCameraOn ? (
|
||||
<>
|
||||
@@ -457,6 +445,35 @@ function AppContent() {
|
||||
{tr("controls.stopVideo")}
|
||||
</button>
|
||||
</div>
|
||||
{/* 设备选择器 */}
|
||||
<div className="video-controls__devices">
|
||||
<div className="device-select-wrapper">
|
||||
<label className="device-select-label">📷</label>
|
||||
<select
|
||||
className="device-select"
|
||||
value={config.cameraDeviceId || "default"}
|
||||
onChange={(e) => switchDevice("camera", e.target.value === "default" ? "" : e.target.value)}
|
||||
>
|
||||
<option value="default">{tr("controls.device.default")}</option>
|
||||
{cameras.map((d) => (
|
||||
<option key={d.deviceId} value={d.deviceId}>{d.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="device-select-wrapper">
|
||||
<label className="device-select-label">🎤</label>
|
||||
<select
|
||||
className="device-select"
|
||||
value={config.micDeviceId || "default"}
|
||||
onChange={(e) => switchDevice("mic", e.target.value === "default" ? "" : e.target.value)}
|
||||
>
|
||||
<option value="default">{tr("controls.device.default")}</option>
|
||||
{mics.map((d) => (
|
||||
<option key={d.deviceId} value={d.deviceId}>{d.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user