news 2026/9/23 12:24:11

使用 Screen-Capturing.js 与 desktopCapture 扩展实现 WebRTC 屏幕捕获:完整集成指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Screen-Capturing.js 与 desktopCapture 扩展实现 WebRTC 屏幕捕获:完整集成指南
  • 示例工程

【免费下载链接】WebRTC-Experiment

WebRTC, WebRTC and WebRTC. Everything here is all about WebRTC!!

项目地址:https://gitcode.com/gh_mirrors/we/WebRTC-Experiment
点击查看免费下载

导读

Screen-Capturing.js 是 WebRTC-Experiment 仓库中用于在网页端捕获屏幕/应用窗口 MediaStream 的前端脚本库,它依赖配套的 Chrome desktopCapture 扩展(Chrome-Extensions/Screen-Capturing.js/Screen-Capturing.js)。本指南将带你走通"下载并改造扩展 → 修改 manifest.json 白名单 → 在页面中引入脚本 → 调用各 API 捕获屏幕与系统音频"的完整链路,并结合仓库源码剖析底层 postMessage 消息协议与 chrome.desktopCapture 调用原理。读完本文,你可以在自己的 HTTPs 域上独立完成 Chrome 屏幕共享的集成与排错。

重要前提:该扩展自 2019 年起已停止维护,原作者明确声明"use at your own risk"。新项目请优先使用浏览器原生的getDisplayMediaAPI,本文中的扩展方案仅适用于需要兼容旧版 Chrome(34+)或需要sourceId细粒度控制的遗留场景。

先看现代替代方案:getDisplayMedia

在深入扩展方案之前,务必先掌握现代浏览器提供的原生屏幕捕获 API。README 开篇即给出推荐的统一封装,它按能力降级依次尝试navigator.getDisplayMedianavigator.mediaDevices.getDisplayMedia,最后才回退到扩展方案:

getScreenStream(function(screenStream) { video.srcObject = screenStream; }); function getScreenStream(callback) { if (navigator.getDisplayMedia) { navigator.getDisplayMedia({ video: true }).then(screenStream => { callback(screenStream); }); } else if (navigator.mediaDevices.getDisplayMedia) { navigator.mediaDevices.getDisplayMedia({ video: true }).then(screenStream => { callback(screenStream); }); } else { getScreenId(function(error, sourceId, screen_constraints) { navigator.mediaDevices.getUserMedia(screen_constraints).then(function(screenStream) { callback(screenStream); }); }); } }

getDisplayMedia由浏览器直接弹窗让用户选择"整个屏幕 / 应用窗口 / 标签页",无需安装任何扩展、无需部署白名单,这是目前唯一推荐的方案。只有当用户浏览器不支持该 API 时,才需要走下文的扩展路径。

工作原理与消息协议(源码剖析)

Screen-Capturing.js 本身不直接调用chrome.desktopCapture,它通过postMessage 消息协议与扩展的 content-script 通信,扩展的 background-script 再调用底层 API 返回sourceId。整条链路由三个脚本协作完成:

脚本角色仓库路径
Screen-Capturing.js网页侧 API 封装,发送/接收 postMessageChrome-Extensions/Screen-Capturing.js/Screen-Capturing.js
content-script.js网页与 background 之间的消息中转站Chrome-Extensions/desktopCapture/content-script.js
background-script.js调用chrome.desktopCapture.chooseDesktopMedia,返回 sourceIdChrome-Extensions/desktopCapture/background-script.js

网页侧:postMessage 的发送与接收

Screen-Capturing.js 在加载时注册message事件监听,并且只处理同源消息:

window.addEventListener('message', function(event) { if (event.origin != window.location.origin) { return; } onMessageCallback(event.data); });

onMessageCallback处理三类响应(见 Screen-Capturing.js):

  • 收到字符串'PermissionDeniedError':用户点了"取消",直接以该字符串回调,便于调用方判断;
  • 收到'rtcmulticonnection-extension-loaded':扩展通知自己已在页面中注入,脚本据此将内部状态chromeMediaSource置为'desktop'
  • 收到携带sourceId的对象:扩展共享了临时 sourceId,同时携带canRequestAudioTrack布尔值,表示本次选择的源是否允许捕获系统音频。

中转站:content-script

content-script.js 维护一个rtcmulticonnectionMessages白名单对象(包含'are-you-there''get-sourceId''audio-plus-tab'三个字符串),只放行白名单内的消息,避免与其他无关 postMessage 冲突:

  • 网页发来'are-you-there'时,content-script 直接回复'rtcmulticonnection-extension-loaded',让网页快速探测扩展存在性;
  • 网页发来'get-sourceId''audio-plus-tab',或携带get-custom-sourceId数组的消息时,通过chrome.runtime.connect()建立的 Port 转发给 background-script;
  • background 返回的消息再通过window.postMessage(message, '*')广播回网页。

底层:background-script 与 chooseDesktopMedia

background-script.js 在chrome.runtime.onConnect中监听 Port 消息,根据消息类型配置screenOptions并调用chrome.desktopCapture.chooseDesktopMedia(screenOptions, port.sender.tab, onAccessApproved)

  • 'get-sourceId':使用默认选项['screen', 'window'],让用户选择整屏或某个应用窗口;
  • 'audio-plus-tab':将选项扩展为['screen', 'window', 'audio', 'tab'],即额外允许捕获标签页和系统音频;
  • { 'get-custom-sourceId': [...] }:使用调用方自定义的选项数组。

用户确认后,onAccessApproved(sourceId, opts)被回调:若sourceId为空(点了取消),回传'PermissionDeniedError';否则回传{ sourceId: sourceId, canRequestAudioTrack: !!opts.canRequestAudioTrack }。这个 sourceId 最终被写入 getUserMedia 的chromeMediaSourceId约束中,从而拿到屏幕 MediaStream。

第一步:下载并改造 desktopCapture 扩展

使用 Screen-Capturing.js 前,必须先准备扩展。仓库中的扩展源码位于 Chrome-Extensions/desktopCapture,你需要自行下载、改造并发布

  1. 下载 desktopCapture 目录全部文件;
  2. 修改 manifest.json 中 content-scripts 的matches白名单,把默认的"https://www.webrtc-experiment.com/*"替换为你自己的域名;
  3. 通过chrome://extensions/以"加载已解压的扩展程序"方式本地测试,或打包成 ZIP 上传 Google Web Store 发布。

manifest.json 关键配置

原仓库 manifest.json 的完整配置如下(version 3.7、manifest_version 2):

{ "name" : "Screen Capturing", "author": "Muaz Khan", "version" : "3.7", "manifest_version" : 2, "minimum_chrome_version": "34", "description" : "Capture full-screen or specific application's screen on any HTTPs domain!", "background": { "scripts": ["background-script.js"], "persistent": false }, "content_scripts": [ { "js": [ "content-script.js" ], "all_frames": true, "run_at": "document_end", "matches": ["https://www.webrtc-experiment.com/*"] }], "icons" : { "48" : "icon.png" }, "permissions": [ "desktopCapture" ], "web_accessible_resources": [ "icon.png" ] }

你需要改动的核心只有matches,例如换成你的域名:

"matches": ["https://www.your-domain.com/*"]

配置要点说明:

  • minimum_chrome_version: "34"chrome.desktopCaptureAPI 自 Chrome 34 起可用,这是扩展的最低兼容版本;
  • permissions中的"desktopCapture":声明使用桌面捕获 API 的权限;
  • all_frames: true:页面中所有 iframe 都会注入 content-script,保证getScreenId这类 iframe 嵌套方案也能工作;
  • background.persistent: false:使用事件驱动(非持久)后台页;
  • web_accessible_resources中的icon.png:网页侧通过chrome-extension://<id>/icon.png探测扩展安装状态时需要使用它(见下文getChromeExtensionStatus原理)。

第二步:在页面中引入 Screen-Capturing.js

扩展就绪后,在你的 HTTPs 页面中引入脚本(仓库同时提供 CDN 用法与本地 index.html 演示页):

<script src="https://www.webrtc-experiment.com/Screen-Capturing.js"></script> <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

若希望离线使用,可直接拷贝仓库中的 Screen-Capturing.js 到你的站点;也可以使用 npm 包:

npm install webrtc-screen-capturing # node_modules/webrtc-screen-capturing/Screen-Capturing.js

脚本同时支持 Chrome 与 Firefox:Firefox 无需扩展,getScreenConstraints会直接返回{ mozMediaSource: 'window', mediaSource: 'window' }约束(见 Screen-Capturing.js)。

API 全览与用法示例

getScreenConstraints

获取可直接传给navigator.mediaDevices.getUserMedia的屏幕捕获约束对象,内部自动完成扩展可用性检查、sourceId 获取与约束组装:

getScreenConstraints(function(error, screen_constraints) { if (error) { return alert(error); } if(screen_constraints.canRequestAudioTrack) { // 本次选择的源支持捕获系统扬声器音频 // getUserMedia({audio:screen_constraints}) } navigator.mediaDevices.getUserMedia({ video: screen_constraints }).then(function(stream) { var video = document.querySelector('video'); video.src = URL.createObjectURL(stream); video.play(); }).catch(function(error) { alert(JSON.stringify(error, null, '\t')); }); });

从源码(Screen-Capturing.js)可以看到其内部组装的约束结构:

var screen_constraints = { mandatory: { chromeMediaSource: chromeMediaSource, // 'screen' 或 'desktop' maxWidth: screen.width > 1920 ? screen.width : 1920, maxHeight: screen.height > 1080 ? screen.height : 1080 }, optional: [] };

其中chromeMediaSource默认是'screen';一旦探测到扩展存在,其值变为'desktop',并且脚本会请求扩展返回 sourceId 后写入screen_constraints.mandatory.chromeMediaSourceIdmaxWidth/maxHeight以屏幕实际分辨率与 1920×1080 中较大者为准。

getScreenConstraintsWithAudio

getScreenConstraints相同,但额外包含系统音频(扬声器)。实现上只是以captureSourceIdWithAudio=true调用getScreenConstraints,进而走getSourceIdWithAudio路径:

getScreenConstraintsWithAudio(function(error, screen_constraints) { if (error) { return alert(error); } navigator.mediaDevices.getUserMedia({ video: screen_constraints, audio: screen_constraints // 必须同时传 audio 这一行 }).then(function(stream) { var video = document.querySelector('video'); video.src = URL.createObjectURL(stream); video.play(); }).catch(function(error) { alert(JSON.stringify(error, null, '\t')); }); });

演示页 index.html 中还给出了更稳妥的写法:先检查screen_constraints.canRequestAudioTrack === true,再决定是否把 audio 约束传出去:

navigator.mediaDevices.getUserMedia({ video: screen_constraints, audio: screen_constraints.canRequestAudioTrack ? screen_constraints : false }).then(...)

getSourceId

直接向扩展索取sourceId(即chromeMediaSourceId),适合进阶用户自行组装约束:

getSourceId(function(sourceId, canRequestAudioTrack) { if(sourceId != 'PermissionDeniedError') { // 拿到 sourceId,组装自己的 getUserMedia 约束 } if(canRequestAudioTrack === true) { // 系统音频(扬声器)可用 } });

注意其实现细节:若sourceId已缓存,会立即用缓存值回调(if(sourceId) return callback(sourceId);),避免重复弹出选择框;否则设置screenCallback并通过window.postMessage('get-sourceId', '*')向扩展请求。

getCustomSourceId

按需指定捕获来源类型,第一个参数必须是数组。支持的格式:

  • window:捕获指定应用窗口
  • screen:捕获整个屏幕
  • tab:捕获标签页
  • audio:捕获系统音频
var our_own_choices = ['tab', 'audio']; getCustomSourceId(our_own_choices, function(sourceId, canRequestAudioTrack) { if(sourceId != 'PermissionDeniedError') { // 你的代码 } if(canRequestAudioTrack === true) { // 系统音频(扬声器)可用 } });

底层通过window.postMessage({ 'get-custom-sourceId': arr }, '*')把数组传给扩展,background-script 收到后将其直接作为chooseDesktopMedia的选项。

getSourceIdWithAudio

getSourceId的"含系统音频"版本,与getScreenConstraintsWithAudio对应:

getSourceIdWithAudio(function(sourceId, canRequestAudioTrack) { if(sourceId != 'PermissionDeniedError') { // 你的代码 } if(canRequestAudioTrack === true) { // 系统音频(扬声器)可用 } });

getChromeExtensionStatus

推荐使用的扩展状态检测方法,比isChromeExtensionAvailable更可靠。它在页面中创建一个指向chrome-extension://<id>/icon.png<img>元素:onload说明扩展已安装,随后再通过 postMessage 握手判断是否启用;onerror说明未安装。可省略参数,此时使用默认扩展 IDajhifddimkapgcifgcodmmfdlknahffk;若使用自己发布的扩展,请传入自己的扩展 ID。

// 传你自己的扩展 ID;不传则使用默认 ID getChromeExtensionStatus('your-extension-id', function(status) { if(status == 'installed-enabled') { // 已安装且已启用 } if(status == 'installed-disabled') { // 已安装但被禁用 } if(status == 'not-installed') { // 未安装 } if(status == 'not-chrome') { // 非 Chrome 浏览器(Firefox 等) } });

注意:在非 Chrome 浏览器(源码中用typeof window.InstallTrigger判断 Firefox)中会直接回调'not-chrome'

isChromeExtensionAvailable

较简化的存在性探测:向扩展发送'are-you-there',2 秒后仍未收到响应则判定不可用:

isChromeExtensionAvailable(function(isAvailable) { if(!isAvailable) alert('Chrome extension is either not installed or disabled.'); });

官方文档建议优先使用getChromeExtensionStatus,因为它能区分"未安装"与"已禁用"两种状态。

常见问题:无法重复捕获屏幕?

屏幕捕获一次后再次调用 API 却不再弹窗?解决办法是先把sourceId置为null,再调用任意 API

sourceId = null; // 关键一行 getScreenConstraints(function(error, screen_constraints) { if (error) { return alert(error); } navigator.mediaDevices.getUserMedia({ video: screen_constraints }).then(function(stream) { var video = document.querySelector('video'); video.src = URL.createObjectURL(stream); video.play(); }).catch(function(error) { alert(JSON.stringify(error, null, '\t')); }); });

原因在源码中可见:getSourceIdgetCustomSourceIdgetSourceIdWithAudio都会检查全局变量sourceId是否已有缓存值,存在则直接复用而不再请求扩展;清空它即可让下一次调用重新触发chooseDesktopMedia选择框。同理,如果你的业务需要"每次都让用户重新选择",可以在回调前主动清理sourceId

进阶替代方案:getScreenId.js(免发布扩展)

如果不想自己发布扩展,仓库还提供了 getScreenId.js 方案。它使用iframe 黑客技巧:页面中的 iframe 从https://www.webrtc-experiment.com/域加载,该域已在官方扩展白名单内,iframe 与扩展通过 postMessage 交换 sourceId,再转发回你的页面,从而让同一个官方扩展在任意 HTTPs 域可用。其完整调用方式与 API(getScreenIdgetChromeExtensionStatus、自定义参数捕获音频/标签页等)见 getScreenId.js/README.md。

<script src="https://www.WebRTC-Experiment.com/getScreenId.js"></script> <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script> <video controls autoplay></video> <script> getScreenId(function (error, sourceId, screen_constraints) { navigator.mediaDevices.getUserMedia(screen_constraints).then(function (stream) { document.querySelector('video').src = URL.createObjectURL(stream); }).catch(function (error) { console.error(error); }); }); </script>

该方案同样有局限:在 iframe 内使用时 postMessage 机制可能失效,官方建议此时改用 WebSocket 或外部服务器中转 sourceId;并且同样需要 HTTPs 环境。

本地运行演示

仓库为演示页提供了极简静态服务器(server.js),基于 Node.js 原生 http 模块,默认监听 9001 端口,仅做静态文件服务并屏蔽对 server.js 自身的访问:

node server.js # Server listening at http://localhost:9001

技术栈与适用场景小结

方案适用场景关键限制
getDisplayMedia现代 Chrome/Edge/Firefox需用户手动选择源;不支持旧版 Chrome 34-70 等
Screen-Capturing.js + 自发布扩展需要固定扩展、细粒度 sourceId 控制的 HTTPs 站点需改 manifest 白名单并发布;扩展已停止维护
getScreenId.js + 官方扩展不想发布扩展、任意 HTTPs 域快速验证依赖官方扩展与 iframe 中转;iframe 内不可用

最后再次提醒:新项目请直接使用getDisplayMedia;Screen-Capturing.js 与 desktopCapture 扩展仅作为历史兼容方案保留。本文所有 API 行为均可对照仓库源码验证:网页侧封装见 Screen-Capturing.js,消息中转见 content-script.js,底层桌面捕获见 background-script.js,完整交互演示见 index.html。

  • 示例工程

【免费下载链接】WebRTC-Experiment

WebRTC, WebRTC and WebRTC. Everything here is all about WebRTC!!

项目地址:https://gitcode.com/gh_mirrors/we/WebRTC-Experiment
点击查看免费下载

相关推荐

上一篇:**探索分布式应用的新纪元:Iroh**
下一篇:黑鸟(Blackbird):Swift中的SQLite轻骑兵

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 12:24:09

风卦避坑指南:3个步骤搞懂源码解析逻辑

风卦避坑指南:3个步骤搞懂源码解析逻辑 复制来的代码跑不通,是不是常让你对着屏幕发呆?报错信息像天书,断点打在哪里都没反应。这时候,别急着删库重造,你需要的是深入源码解析。 很多转岗的朋友觉得“风卦”是个玄学概念,或者觉得它离后端开发很远。其实,“风卦”在技术语境下,常被用作一种隐喻,指代…

作者头像 李华
网站建设 2026/9/23 12:23:53

竞争分析入门:新手避坑指南,3个步骤跑通代码

竞争分析入门:新手避坑指南,3个步骤跑通代码 刚拿到一段网上复制的竞争分析脚本,双击运行直接报错?别慌,这种“复制粘贴就崩”的情况,90%的新手都踩过。问题往往不在代码本身,而在你对底层逻辑的误判和环境配置的疏漏。今天咱们不整虚的,直接拆解微服务架构下竞争分析的实战痛点,帮你把那些坑一个个填平。…

作者头像 李华
网站建设 2026/9/23 12:23:50

男人三字经图解原理,3步搞定性能优化面试

男人三字经图解原理,3步搞定性能优化面试 配置环境就卡半天?别慌,这不是你手笨,是你没看懂底层的【图解原理】。很多后端同学在准备面试时,死记硬背“男人三字经”式的口诀,结果一遇到性能调优的实际场景,脑子一片空白。今天咱们不整虚的,直接拆解这个高频考点,把抽象的概念具象化。 考点梳理:别把口诀当死理…

作者头像 李华
网站建设 2026/9/23 12:23:49

2026最新北京国税电子税务局接口联调5大坑点与避坑指南

2026最新北京国税电子税务局接口联调5大坑点与避坑指南 面试被问“北京国税电子税务局对接原理”时,你是不是只能答出“调接口传数据”,却说不清底层报文加密、签名验证和异步回执处理的细节?2026年最新的税务数字化改造后,很多老代码直接报500错误,现场排查时往往因为不懂原理而手足无措。…

作者头像 李华
网站建设 2026/9/23 12:23:24

国内英文性能优化实战:3步打造速查手册,告别文档翻找

国内英文性能优化实战:3步打造速查手册,告别文档翻找 写代码时最痛苦的不是写不出,而是找资料太慢。官方文档太长抓不住重点,每次遇到国内英文相关的配置或接口,都要在冗长的页面里来回滚动。我花了一周时间,把分散在各处的关键点整理成一份 速查手册 ,效率直接翻倍。 性能瓶颈:为什么“找”比“写”更耗时…

作者头像 李华
网站建设 2026/9/23 12:23:21

数字图片1图解原理:3步搞定项目落地

数字图片1图解原理:3步搞定项目落地 别再对着文档干瞪眼了。你明明看了一堆教程,觉得每个代码都懂,一上手写项目就卡壳,连个简单的图片加载都调不通?这就是典型的“懂了但不会做”。今天不聊虚的,咱们直接拆解 数字图片1 在Web开发中的核心逻辑,用 图解原理 的方式,把这块硬骨头嚼碎了喂给你。…

作者头像 李华