feat: 优化 AudioWorklet 加载策略,增加多种 URL 支持和详细错误日志
This commit is contained in:
@@ -484,25 +484,60 @@ export class S2sWsRealtimeClient extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
// AudioWorklet 加载(WebView2/打包兼容):
|
||||
// 源码已由 Vite `?raw` 内联进 bundle,运行时直接从内存字符串建 Blob → addModule。
|
||||
// 彻底摆脱运行时 fetch/网络/CORS/后端可达性依赖,任何 WebView2 都能加载。
|
||||
const addWorkletFromSrc = async (name, src) => {
|
||||
const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
|
||||
// AudioWorklet 加载(WebView2/打包兼容)——多策略 + 详细日志:
|
||||
// 源码已由 Vite `?raw` 内联进 bundle。不同 WebView2 版本对
|
||||
// blob: / data: / http: 三种 URL 的 addModule 支持不一致,逐一尝试,
|
||||
// 并打印每次失败的真实 name/message,便于定位真正原因。
|
||||
// 策略1 内联源码 → Blob URL(无网络,多数环境最稳)
|
||||
// 策略2 内联源码 → data: URL(部分 WebView2 拒绝 blob: 但接受 data:)
|
||||
// 策略3 后端真实 HTTP(IP 可达 + CORS *,直连 addModule)
|
||||
const attempts = [];
|
||||
const tryModule = async (label, url) => {
|
||||
try {
|
||||
await ctx.audioWorklet.addModule(url);
|
||||
return true;
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (err) {
|
||||
const detail = `${err?.name ?? ""}: ${err?.message ?? String(err)}`;
|
||||
attempts.push(`${label} -> ${detail}`);
|
||||
console.warn(`[ws] worklet addModule 失败(${label}):`, err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const addWorkletFromSrc = async (name, src) => {
|
||||
// 策略1:Blob URL
|
||||
const blobUrl = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
|
||||
let ok = await tryModule(`${name}@blob`, blobUrl);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
if (ok) return true;
|
||||
|
||||
// 策略2:data: URL(base64,UTF-8 安全)
|
||||
try {
|
||||
const b64 = btoa(unescape(encodeURIComponent(src)));
|
||||
ok = await tryModule(`${name}@data`, `data:text/javascript;base64,${b64}`);
|
||||
if (ok) return true;
|
||||
} catch (err) {
|
||||
attempts.push(`${name}@data-encode -> ${err?.message ?? String(err)}`);
|
||||
}
|
||||
|
||||
// 策略3:后端 HTTP(IP 可达 + CORS *)
|
||||
const httpBase = window.VOICE_WORKLETS_BASE;
|
||||
if (httpBase) {
|
||||
ok = await tryModule(`${name}@http`, `${httpBase}${name}.js`);
|
||||
if (ok) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const workletOk =
|
||||
(await addWorkletFromSrc("mic-capture", micWorkletSrc)) &&
|
||||
(await addWorkletFromSrc("audio-playback", playbackWorkletSrc));
|
||||
if (workletOk) {
|
||||
console.info("[ws] AudioWorklet 已通过内联源码 Blob URL 加载(WebView2 兼容模式)");
|
||||
console.info("[ws] AudioWorklet 加载成功(WebView2 兼容模式)");
|
||||
} else {
|
||||
throw new Error("AudioWorklet 模块加载失败(内联源码 Blob addModule 失败)");
|
||||
throw new Error(
|
||||
`AudioWorklet 模块加载失败。尝试详情:${attempts.join(" | ") || "无"}`,
|
||||
);
|
||||
}
|
||||
|
||||
const captureNode = new AudioWorkletNode(ctx, "mic-capture", {
|
||||
|
||||
Reference in New Issue
Block a user