屏幕共享实现:桌面采集API、窗口选择机制、性能优化技巧

屏幕共享,说白了就是把你的电脑桌面或者某个窗口,实时地传给对方。这个功能在远程会议、在线教育、游戏直播里太常见了。我最早接触这块是在做一款远程协作工具的时候,当时踩了不少坑,今天我把这些经验都掏出来给你。

桌面采集API:getDisplayMedia

WebRTC 里做屏幕共享,核心 API 就一个——getDisplayMedia()。它是浏览器原生提供的,专门用来捕获屏幕内容。

// 最简单的调用
const stream = await navigator.mediaDevices.getDisplayMedia({
  video: true,
  audio: false
});

// 然后像普通视频流一样处理
const pc = new RTCPeerConnection();
stream.getTracks().forEach(track => pc.addTrack(track, stream));

嗯,这里要注意:getDisplayMedia 必须在用户手势事件(比如点击按钮)中调用,否则浏览器会拒绝。我刚开始做的时候,在页面加载时自动调用,结果死活弹不出选择窗口,后来才意识到是安全策略的限制。

关键点:getDisplayMedia 返回的 MediaStream 包含视频轨道,音频轨道默认是 false。如果你想同时采集系统音频,需要设置 audio: true,但浏览器支持度不一。

窗口选择机制:用户说了算

调用 getDisplayMedia 后,浏览器会弹出一个选择对话框,让用户自己选——是整个屏幕、某个应用窗口,还是浏览器标签页。这个选择权必须交给用户,你不能在代码里强行指定。

但你可以通过 displaySurface 约束来提示浏览器优先展示哪种类型:

const constraints = {
  video: {
    displaySurface: "window" // 优先显示窗口列表
  }
};

const stream = await navigator.mediaDevices.getDisplayMedia(constraints);

displaySurface 有三个可选值:monitor(整个屏幕)、window(应用窗口)、browser(浏览器标签页)。注意这只是提示,最终选择权还是在用户手里。

我的经验:如果你做的是远程协助类应用,建议默认提示用户选择"整个屏幕",因为这样画面最完整。如果是会议场景,提示"窗口"更合适,用户可以选择只共享PPT或文档。

性能优化技巧:别让CPU爆炸

屏幕共享最头疼的问题就是性能。你想想看,桌面可能是个4K屏,每秒60帧,直接编码传输的话,CPU直接拉满。我遇到过用户反馈"一共享屏幕电脑就卡死",后来总结了几条优化策略。

1. 控制帧率

屏幕共享不需要60fps,15fps就够用了。除非你在共享游戏画面,否则降低帧率能大幅减少编码压力。

const constraints = {
  video: {
    frameRate: { ideal: 15, max: 30 }
  }
};

2. 限制分辨率

采集原始分辨率然后硬编码,是最傻的做法。我建议先降分辨率再编码,比如采集1080p,编码时降到720p。

const constraints = {
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
};

3. 动态调整码率

根据网络状况动态调整视频码率,这个在 WebRTC 里可以通过 RTCRtpSender.setParameters() 实现。

const sender = pc.getSenders().find(s => s.track.kind === 'video');
const params = sender.getParameters();
params.encodings[0].maxBitrate = 500000; // 500kbps
await sender.setParameters(params);
避坑指南:我曾经在低配笔记本上测试,发现共享屏幕时风扇狂转。后来加了分辨率限制和帧率控制,CPU占用从80%降到了30%。记住:宁可画面模糊一点,也别让用户电脑卡死。

知识体系总览

下面这张图帮你理清屏幕共享的核心流程和优化点:

屏幕共享核心流程 用户点击共享按钮 getDisplayMedia() 弹出选择框 整个屏幕 (monitor) 应用窗口 (window) 浏览器标签 (browser) 性能优化三件套 帧率控制 (15fps) 分辨率限制 (720p) 动态码率调整

实际项目中的避坑指南

做屏幕共享,光知道 API 还不够,实战中坑特别多。我挑几个典型的说说。

  • 权限问题:用户第一次拒绝后,再次调用 getDisplayMedia 可能不会弹出选择框。我建议在 UI 上给用户明确的提示,引导他们手动触发。
  • 停止共享检测:用户点击浏览器的"停止共享"按钮后,你需要监听 track.onended 事件来做清理工作。
const track = stream.getVideoTracks()[0];
track.onended = () => {
  console.log('用户停止了屏幕共享');
  // 清理连接、更新UI等
};
  • 多显示器场景:如果用户有多个显示器,getDisplayMedia 会让用户选择共享哪个屏幕。你可以在约束里设置 displaySurface: "monitor" 来提示。
小技巧:在共享屏幕时,建议同时采集麦克风音频,这样用户既能共享画面又能讲解。用 getUserMedia({ audio: true }) 单独采集麦克风,然后通过 pc.addTrack 添加到同一个 PeerConnection 里。

性能对比表

不同场景下,我建议的配置参数如下:

场景 帧率 分辨率 码率
PPT/文档共享 5-10 fps 720p 300-500 kbps
代码演示 10-15 fps 1080p 500-800 kbps
视频播放 15-24 fps 1080p 1-2 Mbps
游戏直播 30 fps 1080p 或 4K 2-5 Mbps

嗯,这个表是我根据实际项目经验总结的。你想想看,共享PPT时画面变化很少,5fps就够;但游戏画面每帧都在变,30fps是底线。

核心总结:屏幕共享的关键就三点——选对 API(getDisplayMedia)、尊重用户选择(窗口选择机制)、做好性能优化(帧率+分辨率+码率)。把这三点吃透,大部分场景都能搞定。

最后说一句,屏幕共享的调试比较麻烦,因为浏览器安全策略限制,本地文件(file://)无法使用 getDisplayMedia。我建议用 localhost 或者 HTTPS 来开发测试。好了,这一章就到这里,下一章我们聊聊更进阶的话题。


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