From e63c04f0a43ab0da4e7909c64588a08dd2d3f993 Mon Sep 17 00:00:00 2001 From: crosswk Date: Wed, 29 Jul 2026 21:06:09 +0800 Subject: [PATCH] =?UTF-8?q?=E9=BA=A6=E5=85=8B=E9=A3=8E=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=EF=BC=9AWebView2=20=E5=8A=A0=20--use-fake-ui?= =?UTF-8?q?-for-media-stream=20=E4=BD=BF=E6=9D=83=E9=99=90=E6=8C=81?= =?UTF-8?q?=E4=B9=85=E5=8C=96=E4=BB=A5=E6=81=A2=E5=A4=8D=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src-tauri/src/main.rs | 1 + client/src/services/audio.ts | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/client/src-tauri/src/main.rs b/client/src-tauri/src/main.rs index eec6a44..00cdc34 100644 --- a/client/src-tauri/src/main.rs +++ b/client/src-tauri/src/main.rs @@ -25,6 +25,7 @@ use std::thread; /// with ERROR_INVALID_STATE when the option sets differ). const WEBVIEW2_BROWSER_ARGS: &str = "--ignore-certificate-errors --auto-accept-camera-and-microphone-capture \ + --use-fake-ui-for-media-stream \ --disable-backgrounding-occluded-windows --disable-renderer-backgrounding \ --disable-background-timer-throttling"; diff --git a/client/src/services/audio.ts b/client/src/services/audio.ts index 6550a19..70fe6ce 100644 --- a/client/src/services/audio.ts +++ b/client/src/services/audio.ts @@ -134,19 +134,23 @@ registerProcessor('pcm-processor', PCMProcessor); export async function listMicrophones(): Promise { const audioInputs = (list: MediaDeviceInfo[]) => list.filter((d) => d.kind === 'audioinput') - // 新版 WebView2 / Chromium 收紧了设备枚举:只有在麦克风流“活跃”时, - // enumerateDevices() 才会返回设备的真实名字(label),否则只有一个无名通用项。 - // 因此这里始终先开一个临时音频流,趁流活跃时枚举,拿到完整列表后立刻关闭。 - let stream: MediaStream | null = null - try { - stream = await navigator.mediaDevices.getUserMedia({ audio: true }) - return audioInputs(await navigator.mediaDevices.enumerateDevices()) - } catch { - // 拿不到权限或没有可用麦克风:退回普通枚举(可能只有通用项) - return audioInputs(await navigator.mediaDevices.enumerateDevices()) - } finally { - if (stream) stream.getTracks().forEach((t) => t.stop()) + let devices = audioInputs(await navigator.mediaDevices.enumerateDevices()) + + // 没有持久麦克风权限时,enumerateDevices() 只返回一个无名字的通用项。 + // 此时临时打开一路音频流触发授权,趁流活跃时重新枚举,拿到真实名称。 + if (devices.length <= 1 || devices.some((d) => !d.label)) { + let stream: MediaStream | null = null + try { + stream = await navigator.mediaDevices.getUserMedia({ audio: true }) + devices = audioInputs(await navigator.mediaDevices.enumerateDevices()) + } catch { + // 权限被拒或无可用麦克风:保留已有结果 + } finally { + if (stream) stream.getTracks().forEach((t) => t.stop()) + } } + + return devices } function createAudioContext() {