Yuan's Blog

Tauri应用视频无法播放?macOS上遇到unsupported URL 的终极解决方案

在苹果电脑上用 Tauri 开发本地应用时,如果你试图通过 <video> 播放本地视频,很可能会遇到以下报错: [Error] Failed to load resource: unsupported URL

或者视频黑屏、音频正常、无明显错误提示。


❗常见错误路径

  • convertFileSrc → ❌ WebKit 不支持 Range 请求
  • fetch → blob → ❌ 触发 CORS,资源被拦截
  • 修改 CSP → ❌ 无法解决底层协议问题

🧠 问题根源

  1. WebKit 限制:macOS 下的 Safari 引擎不支持 asset:// 协议播放 <video>,但能显示图片等静态资源。
  2. Range 请求失败:视频默认发起 Range 请求(如 bytes=102400-),asset:// 无法返回分段内容。
  3. CORS 限制fetch("asset://...") 被视为跨域,无法设置 Access-Control-Allow-Origin。
  4. 协议未注册stream:// 协议未在 Rust 注册 handler 时会报错或静默失败。
  5. 渲染异常:即使加载成功,有时视频黑屏,可能是 CSS 或编解码兼容性问题。

🧪 重现示例

fetch("asset://video.mp4")
  .then(res => res.blob())
  .then(blob => video.src = URL.createObjectURL(blob));

结果: [Error] Failed to load resource: unsupported URL

WebKit 不支持 asset:// 是因为:

  • asset:// 是 Tauri 内置的只读协议,它底层用的是 AppHandle::fs_scope();
  • 这个协议的行为是固定的,你无法控制它的响应方式;
  • 它不支持 Range 请求;
  • 它没有标准的 MIME negotiation;
  • 它不能添加正确的 CORS header;
  • WebKit 对不认识的协议直接判定为 unsupported URL,尤其在 这种有强要求的组件中。

✅正确做法:自定义 stream:// 协议

✅ WebKit 支持 stream:// 是因为:

  • stream:// 是由 你自己在 Tauri 中注册的自定义协议;
  • 你可以在注册的时候告诉 WebKit 如何处理这个协议,包括:
  • 设置 MIME 类型(告诉浏览器“这是视频”);
  • 支持 Range 请求(用于 分段加载);
  • 支持 跨源访问(CORS);
  • 甚至你可以添加自定义 header、缓存等逻辑。

👉 本质上,stream:// 是你控制的,所以你可以让它“长得像”一个浏览器喜欢的协议。

✅ 步骤 1:在 Rust 后端注册协议处理器

tauri::Builder::default()
  .register_uri_scheme_protocol("stream", |request| {
    // 读取视频文件
    // 处理 Range 请求
    // 返回正确的 Content-Type(例如 video/mp4)和响应体
  })

✅ 步骤 2:前端使用 stream 协议加载视频 <video controls src="stream://video.mp4" />

🧰 为什么图片/字体能加载但视频失败?

  • 图片 / 字体 / 小脚本,体积小,一次加载(无需 Range)。
  • 视频 / 音频,体积大,分段加载(需要 Range)、缓存、解码。

元素在加载时默认发起 Range 请求,例如: Range: bytes=102400- 而 asset:// 没法返回部分内容,只能一次性返回整个文件,因此无法支持断点播放或拖动。

🧠 Tauri 应用运行原理(简要说明)

🧩 前端部分:

  • 你写的 React/Vue 页面,会被打包为静态资源
  • 使用 asset:// 协议在 WebView 中加载

🔧 后端部分:

  • Rust 提供本地访问能力(文件、FFmpeg、数据库等)
  • 可注册协议(如 stream://)模拟 HTTP 服务

🎞️ 渲染部分:

  • macOS → WebKit (Safari 内核)
  • Windows → WebView2 (Chromium)

🙋‍♂️ 常见问题答疑(FAQ)

❓为什么不能直接用 file:/// 或 asset://?

因为这些协议: • 不支持 Range 请求 • 无法处理 MIME negotiation • fetch 触发 CORS 被浏览器拦截

❓视频播放黑屏但有声音,可能是哪里的问题?

可能原因: • CSS:video 被遮挡或透明 • 编码格式:不支持某种视频流(如 H.264 10bit) • WebKit 渲染 Bug:只能播放音频轨

✅ 总结

💡 如果你在 Tauri 中需要播放本地大文件(视频/音频等),你必须实现一个 支持 Range 请求的自定义协议 handler。只有这样才能绕过 WebKit 的限制,避免 CORS 和 MIME 问题。