6、屏幕共享:getDisplayMedia()方法、屏幕共享的约束与权限、录制屏幕内容
屏幕共享,说白了就是把你的电脑屏幕内容实时传给对方。这在远程会议、在线教育、游戏直播里太常见了。WebRTC 做这件事,核心就靠一个 API——getDisplayMedia()。
我个人习惯把屏幕共享分成三步:获取流 → 约束权限 → 录制或传输。咱们一步步拆开看。
核心知识点速览
- getDisplayMedia() 是浏览器提供的屏幕捕获 API
- 约束参数控制分辨率、帧率、光标显示等
- 权限由浏览器弹窗管理,用户主动选择
- MediaRecorder 可录制屏幕内容为视频文件
6.1 getDisplayMedia() 的基本用法
这个 API 和 getUserMedia() 很像,但有个关键区别:它不要求用户提前授权摄像头或麦克风。调用时浏览器会弹出一个选择窗口,让用户自己选「共享整个屏幕、某个窗口还是某个浏览器标签页」。
来看最简代码:
async function startScreenShare() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true
});
// 拿到 stream 后,可以挂到 video 标签上预览
const video = document.getElementById('preview');
video.srcObject = stream;
} catch (err) {
console.error('屏幕共享失败:', err);
}
}
嗯,这里要注意:getDisplayMedia() 返回的 MediaStream 默认只包含视频轨道,没有音频。如果你想同时捕获系统音频(比如电脑播放的声音),需要额外指定 audio: true。
💡 个人经验
我在项目中遇到过一个问题:用户共享窗口时,如果窗口被最小化或遮挡,捕获的画面会变成黑屏或卡住最后一帧。解决办法是监听 track.onended 事件,及时提示用户恢复窗口或重新选择。
6.2 屏幕共享的约束参数
约束参数和 getUserMedia() 类似,但有一些专属选项。我习惯把约束分成两类:视频质量约束和显示策略约束。
视频质量约束
| 参数 | 说明 | 示例值 |
|---|---|---|
| width | 期望宽度(像素) | { min: 1280, ideal: 1920, max: 3840 } |
| height | 期望高度(像素) | { min: 720, ideal: 1080, max: 2160 } |
| frameRate | 帧率(fps) | { ideal: 30, max: 60 } |
| displaySurface | 默认捕获类型 | 'monitor' | 'window' | 'browser' |
显示策略约束
cursor 参数控制鼠标光标是否显示在共享画面中。这个细节容易被忽略,但实际体验影响很大。
const constraints = {
video: {
cursor: 'always', // 'always' | 'motion' | 'never'
displaySurface: 'monitor', // 默认选整个屏幕
logicalSurface: true, // 是否捕获被遮挡的内容
},
audio: {
echoCancellation: true,
noiseSuppression: true
}
};
const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
⚠️ 避坑指南
我曾经在做一个远程协作工具时,发现共享的屏幕画面模糊不清。排查了半天,原来是 logicalSurface 默认是 false,导致浏览器只捕获窗口的可见部分。如果你的应用需要捕获被其他窗口遮挡的内容,记得把这个参数设为 true。
6.3 权限管理:用户说了算
屏幕共享的权限和摄像头麦克风不一样。它没有「永久授权」的概念——每次调用 getDisplayMedia() 都会弹出选择窗口,用户必须手动点击「共享」才能继续。
为什么会这样?因为屏幕内容太敏感了。你想想看,如果网页能静默捕获屏幕,那密码、聊天记录、银行卡号全都能被偷走。所以浏览器强制要求用户主动确认。
权限相关的几个关键点:
- 无法提前授权:没有类似
navigator.permissions.query({name: 'display-capture'})的 API - 用户可随时停止:浏览器会在地址栏显示共享图标,点击即可停止
- 监听停止事件:通过
track.onended感知用户主动停止
const track = stream.getVideoTracks()[0];
track.onended = () => {
console.log('用户停止了屏幕共享');
// 清理 UI 状态,比如隐藏预览、释放资源
stopScreenShare();
};
6.4 录制屏幕内容
拿到屏幕流之后,录制就顺理成章了。WebRTC 本身不提供录制功能,但浏览器自带的 MediaRecorder API 可以轻松搞定。
我常用的录制方案:
let mediaRecorder;
let recordedChunks = [];
function startRecording(stream) {
recordedChunks = [];
// 指定 MIME 类型,优先用 webm
const options = { mimeType: 'video/webm; codecs=vp9' };
mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
recordedChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
// 可以下载或预览
const a = document.createElement('a');
a.href = url;
a.download = 'screen-recording.webm';
a.click();
};
mediaRecorder.start(1000); // 每秒生成一个 chunk
}
function stopRecording() {
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
mediaRecorder.stop();
}
}
💡 录制小技巧
如果你需要同时录制屏幕和麦克风声音,可以用 AudioContext 把两个音频轨道混音。我做过一个教学工具,就是先把屏幕流和麦克风流合并成一个 MediaStream,再传给 MediaRecorder,这样录出来的视频既有画面又有讲解声。
6.5 实际项目中的注意事项
屏幕共享看起来简单,但坑不少。我总结几个常见问题:
- 分辨率适配:用户屏幕可能是 4K,但网络带宽不够。建议用
ideal而不是exact,让浏览器自动降级 - 多显示器场景:
getDisplayMedia()默认让用户选择共享哪个显示器,你无法强制指定 - 性能开销:高帧率录制会消耗大量 CPU,移动端尤其明显。我一般把帧率限制在 15-24fps
- 浏览器兼容性:Safari 对
getDisplayMedia()支持较晚,而且不支持cursor参数
核心要点回顾
- getDisplayMedia() 获取屏幕流,用户必须手动授权
- 约束参数控制分辨率、帧率、光标显示策略
- 权限不可静默获取,用户可随时停止共享
- MediaRecorder 配合屏幕流可实现录制功能
- 注意性能优化和浏览器兼容性差异
屏幕共享这块,说白了就是「获取流 + 处理流」。掌握了 getDisplayMedia() 的约束和权限机制,再配合 MediaRecorder,你就能做出一个完整的屏幕共享和录制功能。我在实际项目中踩过的坑都写在上面了,希望能帮你少走弯路。