getdisplaymedia 调用需 https/localhost 环境且用户手势触发,常见错误为 notallowederror 和 notfounderror;录制需 mediarecorder 配合 video/webm;codecs=vp9,注意 blob url 及时释放与 safari 兼容性。

getDisplayMedia 调用失败:常见报错和权限前提
直接调用 navigator.mediaDevices.getDisplayMedia() 会失败,不是代码写错了,而是浏览器根本没给机会——它要求页面必须是 HTTPS 或 localhost 环境,HTTP 页面(哪怕本地 file://)一律拒绝。Chrome、Edge 从 92 版本起还强制要求用户手势触发(比如 click、tap),不能在页面加载时自动调用。
常见错误信息包括:NotAllowedError: Permission denied(HTTPS 缺失或未手动触发)、NotFoundError: Requested device not found(用户取消授权后再次调用未重试权限流程)。
- 确保服务跑在
https://或http://localhost上(可用python3 -m http.server 8000快速起本地服务) - 绑定到按钮点击事件,不要在
DOMContentLoaded里直接调;如果需要“自动”启动,至少加个遮罩层引导用户点一次 - 捕获
NotAllowedError后,别静默吞掉,提示用户“请允许屏幕共享,并确保您点击了开始按钮”
录制整个屏幕还是单个窗口?track 的 constraints 怎么设
getDisplayMedia() 返回的是一个 MediaStream,里面默认只含视频轨(video:true),不带音频。是否录系统声音、是否限制为当前窗口、是否允许选择标签页——全靠 constraints 参数控制。
关键区别在于:video: { mediaSource: 'screen' } 弹出的是“整个屏幕/窗口/标签页”三级选择框;而 video: true(不指定 mediaSource)在部分 Chrome 版本中会退化为仅允许选窗口,且不显示“整个屏幕”选项。
- 要支持“整个屏幕”,必须显式传
{ video: { mediaSource: 'screen' } } - 想同时录系统音频(仅 Chromium 内核支持),加
audio: true,但注意:这会额外弹一个麦克风权限请求,且用户可能误点拒绝;实际生效需用户在共享选择框里勾选“包含音频”复选框(Chrome 95+) - 禁止用户选标签页(只让选窗口或屏幕),加
video: { mediaSource: 'window', preferCurrentTab: false },但preferCurrentTab并非标准属性,兼容性差,不建议依赖
拿到 stream 后怎么真正录成文件?MediaRecorder 是唯一靠谱路径
很多人以为 getDisplayMedia() 返回 stream 就等于“已录制”,其实它只是实时视频流源,不保存、不编码、不生成文件。必须用 MediaRecorder 接入才能转成可下载的 blob。
注意:Chrome 对 MediaRecorder 的 MIME 类型支持有限,video/webm;codecs=vp9 兼容最好;video/mp4 在 Safari 和部分 Chrome 版本中会失败(报 Failed to construct 'MediaRecorder': InvalidStateError)。
- 创建 recorder 前先检查支持性:
MediaRecorder.isTypeSupported('video/webm;codecs=vp9') - start() 前务必监听
dataavailable事件,否则 blob 不会产出;每个dataavailable触发一次,对应一段 chunk,最后用new Blob(chunks, {type: 'video/webm'})合并 - 不要等 stop() 才收集数据——有些浏览器在 stop() 前就触发最后一次
dataavailable,有些则在 stop() 后才触发,稳妥做法是每次dataavailable都 push 到数组
停止录制后下载失败?Blob URL 生命周期和内存泄漏风险
用 URL.createObjectURL(blob) 创建下载链接后,如果不手动 URL.revokeObjectURL(),blob 会一直驻留在内存里,反复录制几次就可能 OOM。更隐蔽的问题是:Safari 对 blob URL 的有效期极短(几秒),还没点下载就 404。
- 下载完成后立即调用
URL.revokeObjectURL(url),哪怕只是a.click()触发下载也要加setTimeout(() => URL.revokeObjectURL(url), 100) - Safari 下载失败时,可 fallback 到
fetch()+response.blob()中转一次(绕过 blob URL),但会多一次异步,延迟略高 - 录制时间过长(>10 分钟)时,
MediaRecorder可能因 buffer 溢出自动停止,需监听stop和error事件并主动处理中断逻辑
真正难的不是调通 getDisplayMedia,而是应对不同浏览器对“音频共享”“MIME 类型”“blob 生命周期”的差异化实现——这些细节不压测到真机,光看文档根本发现不了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











