29、屏幕共享常见问题:黑屏问题、权限拒绝、编解码器不支持、跨域问题

屏幕共享这个功能,说实话,写代码的时候挺爽的。但一到真机测试,各种问题就冒出来了。我这些年做WebRTC,光是屏幕共享的坑就踩了不下十次。今天我把最常见的四个问题拎出来,一个一个说清楚。

为什么会黑屏?为什么用户点了允许还是没画面?为什么有的浏览器能跑有的不行?嗯,咱们一个个拆。

29.1 黑屏问题

黑屏,是屏幕共享里最让人头疼的问题。没有之一。

我遇到过好几次,代码写完了,一跑,画面是黑的。用户那边也点了“共享屏幕”,但就是啥也看不到。这时候别慌,先排查下面几个点。

29.1.1 常见原因

  • MediaStream 没有正确获取getDisplayMedia() 返回的 Promise 可能被 reject 了,但你忘了 catch。
  • video 标签没有设置 autoplay:或者设置了但被浏览器策略阻止了。
  • canvas 绘制时跨域问题:如果你把屏幕流画到 canvas 上,再转成其他格式,canvas 会被“污染”。
  • 编解码器不匹配:发送端用了 H.264,接收端只支持 VP8,画面直接黑掉。

29.1.2 排查步骤

我个人习惯,遇到黑屏先做三件事:

  1. 打开浏览器控制台,看有没有报错。
  2. 检查 mediaStream.getVideoTracks()[0] 是否存在。
  3. ontrack 回调里打印一下 track.kindtrack.enabled

核心检查代码

// 获取屏幕流
navigator.mediaDevices.getDisplayMedia({ video: true })
  .then(stream => {
    console.log('屏幕流获取成功,track数量:', stream.getTracks().length);
    // 检查视频轨道
    const videoTrack = stream.getVideoTracks()[0];
    if (!videoTrack) {
      console.error('没有视频轨道!');
      return;
    }
    console.log('视频轨道状态:', videoTrack.readyState, videoTrack.enabled);
    // 绑定到 video 标签
    localVideo.srcObject = stream;
  })
  .catch(err => {
    console.error('获取屏幕流失败:', err.name, err.message);
  });

小技巧:如果黑屏但控制台没报错,试试在 video 标签上加 muted 属性。有些浏览器不允许非静音视频自动播放。

29.1.3 我曾经踩过的坑

有一次,我在一个 Electron 项目里做屏幕共享。本地测试一切正常,打包后用户反馈黑屏。查了两天,最后发现是 getDisplayMedia 在 Electron 的渲染进程里被安全策略拦截了。解决方案是用主进程的 desktopCapturer 来获取屏幕流。嗯,这个坑我记了很久。

29.2 权限拒绝

权限拒绝,说白了就是用户点了“拒绝”或者浏览器弹窗被屏蔽了。但有时候用户明明点了“允许”,还是报权限错误。这是怎么回事?

29.2.1 用户主动拒绝

这个最简单。用户点了拒绝,getDisplayMedia 返回的 Promise 会 reject,错误类型是 NotAllowedError

navigator.mediaDevices.getDisplayMedia({ video: true })
  .catch(err => {
    if (err.name === 'NotAllowedError') {
      // 用户拒绝了权限
      showToast('请允许屏幕共享权限');
    }
  });

29.2.2 浏览器权限策略

你想想看,有些浏览器要求屏幕共享必须在用户手势(比如点击按钮)的上下文中触发。如果你在页面加载完成后自动调用 getDisplayMedia,浏览器会直接拒绝。

注意getDisplayMedia 必须在用户手势事件(click、touchstart 等)中调用。异步回调里调用也不行。

29.2.3 权限被浏览器设置禁用

我在项目中遇到过,用户用的是公司电脑,IT 部门在组策略里禁用了屏幕共享。这种情况前端无能为力,只能提示用户联系管理员。

29.3 编解码器不支持

编解码器的问题,说白了就是发送端和接收端“说不到一块去”。你发的是 H.264,我只懂 VP8,那画面就出不来。

29.3.1 如何检测支持的编解码器

WebRTC 提供了 RTCRtpSender.getCapabilities()RTCRtpReceiver.getCapabilities() 来查询。

// 查询发送端支持的视频编解码器
const videoCapabilities = RTCRtpSender.getCapabilities('video');
if (videoCapabilities) {
  console.log('支持的编解码器:');
  videoCapabilities.codecs.forEach(codec => {
    console.log(codec.mimeType);
  });
}

29.3.2 协商时的处理

我个人习惯,在创建 Offer 时指定优先使用的编解码器。如果接收端不支持,SDP 协商时会自动降级。

const pc = new RTCPeerConnection();
const transceiver = pc.addTransceiver('video', {
  direction: 'sendonly',
  sendEncodings: [
    { codec: 'video/H264' }
  ]
});

重要:不是所有浏览器都支持 H.264。Chrome 支持,但 Firefox 默认用 VP8。如果你要做跨浏览器屏幕共享,建议两端都支持 VP8 或 VP9。

29.3.3 我曾经遇到的案例

有一次,用户用 Safari 共享屏幕,接收端是 Chrome。画面一直黑屏。查了半天,发现 Safari 的屏幕共享默认输出的是 H.264,但 Chrome 接收端没有启用 H.264 解码。解决方案是在 Chrome 的 RTCPeerConnection 配置里显式启用 H.264。

29.4 跨域问题

跨域问题,在屏幕共享里其实不常见,但一旦出现就很隐蔽。它通常发生在你把屏幕流绘制到 canvas 上,或者用 MediaRecorder 录制的时候。

29.4.1 canvas 被污染

如果你把屏幕流画到 canvas 上,然后调用 canvas.toDataURL()canvas.toBlob(),浏览器会抛出一个安全错误。因为屏幕流的内容可能包含跨域资源(比如用户打开了另一个域的页面)。

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 把屏幕流画到 canvas 上
ctx.drawImage(video, 0, 0);
// 下面这行会报错:Tainted canvases may not be exported
const dataUrl = canvas.toDataURL();

解决方案:如果不需要导出 canvas 内容,可以忽略这个错误。如果确实需要导出,可以考虑用 OffscreenCanvas 或者 WebGL 来绕过。

29.4.2 跨域 iframe 中的屏幕共享

如果你的页面嵌在 iframe 里,而且 iframe 的域名和父页面不同,getDisplayMedia 可能无法正常工作。这是因为浏览器对 iframe 的权限控制更严格。

我建议,屏幕共享功能尽量在主页面实现,不要在 iframe 里搞。如果非要在 iframe 里做,记得设置 allow="display-capture" 属性。

<iframe src="https://example.com/screen-share" allow="display-capture"></iframe>

29.5 知识体系总览

下面这张图,我把屏幕共享的四个常见问题以及它们的排查路径整理了一下。你可以把它当成一个速查表。

屏幕共享常见问题排查总览 黑屏问题 权限拒绝 编解码器不支持 跨域问题 排查路径 1. 检查 MediaStream 是否获取 2. 检查 video autoplay 3. 检查 canvas 跨域污染 排查路径 1. 检查用户是否主动拒绝 2. 检查是否在用户手势中 3. 检查浏览器策略 排查路径 1. 查询 getCapabilities() 2. 检查 SDP 协商日志 3. 指定优先编解码器 排查路径 1. 检查 canvas 导出操作 2. 检查 iframe 权限 3. 使用 OffscreenCanvas 核心原则 先看控制台 → 再查权限 → 最后看编解码器协商 跨域问题通常出现在 canvas 和 iframe 场景中

29.6 写在最后

屏幕共享的这四个问题,其实都有固定的排查套路。黑屏先看流有没有拿到,权限拒绝先看是不是用户手势触发的,编解码器不支持就看 SDP 协商日志,跨域问题基本只出现在 canvas 和 iframe 里。

我做了这么多年 WebRTC,最大的感受就是:别怕出问题,怕的是不知道怎么查。上面这些排查路径,都是我一个个坑踩出来的。你照着走一遍,90% 的问题都能定位到。

我的建议:在开发阶段就把这些错误处理逻辑写好。不要等到用户反馈了再补。尤其是权限拒绝和编解码器不支持,提前做好降级方案,用户体验会好很多。


公众号:蓝海资料掘金营,微信deep3321