Chrome扩展录制屏幕与麦克风时出现DOMException: Failed due to shutdown错误
错误根因
- 违反Chrome媒体权限调用规则:你的逻辑是页面加载后content.js自动发起麦克风权限请求,申请成功后直接触发background.js自动调用
getUserMedia,整个流程没有任何用户主动交互(比如点击按钮)作为触发前提,Chrome会直接拦截这类无用户交互的媒体访问请求,抛出DOMException: Failed due to shutdown错误。 - 逻辑链路不完整:你当前的background.js仅处理了content.js发送的success消息,完全没有适配popup点击发送的
start/stop控制消息,录制逻辑和用户操作完全脱节。 - 缺失必要权限:manifest.json中未声明
audioCapture麦克风捕获权限,权限不足也会导致媒体请求被拦截。 - 冗余的重复调用:content.js已经完成权限申请的前提下,background.js无需重复发起相同的
getUserMedia请求,额外增加了异常概率。
解决方案
1. 修改manifest.json补充权限
在permissions数组中新增麦克风捕获权限,同时补上缺失的popup入口配置:
{ "name": "My Recorer", "version": "1.0", "manifest_version": 2, "background": { "scripts": ["background.js"], "persistent": true }, "browser_action": { "default_popup": "popup.html", "default_icon": "test.png" }, "commands": { "run-foo": { "suggested_key": { "default": "Ctrl + Shift + Y", "mac": "Command+Shift+Y" }, "description": "Run \"foo\" on the current page." }, "_execute_action": { "suggested_key": { "default": "Ctrl + Shift + Y", "mac": "Command+Shift+Y" } } }, "permissions": [ "activeTab", "clipboardWrite", "declarativeContent", "storage", "tabs", "tabCapture", "desktopCapture", "alarms", "downloads", "audioCapture", "<all_urls>" ] }
2. 删除content.js的自动请求逻辑
直接删除content.js中原有的自动调用getUserMedia的代码,避免无交互的权限请求触发拦截。如果后续需要注入页面逻辑可以保留content.js文件,但不要在页面加载时自动发起媒体请求。
3. 重写background.js逻辑
将媒体请求和录制逻辑全部绑定到popup点击触发的消息回调中,同时支持屏幕+麦克风音频合并录制:
var screenRecorder = null; var screenChunks = []; let micStream = null; chrome.runtime.onMessage.addListener(gotMessage); async function gotMessage(message) { if(message.from === 'start') { try { // popup点击属于合法用户交互,此时调用getUserMedia不会被拦截 micStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true } }); // 获取当前标签页的屏幕流,可根据需求替换为desktopCapture录全屏 const screenStream = await chrome.tabCapture.capture({ audio: true, video: true }); // 合并麦克风音频和屏幕音频 const combinedAudioTracks = [...screenStream.getAudioTracks(), ...micStream.getAudioTracks()]; const combinedStream = new MediaStream([...screenStream.getVideoTracks(), ...combinedAudioTracks]); // 初始化录制器 const options = { mimeType: 'video/webm;codecs=vp9,opus' }; screenRecorder = new MediaRecorder(combinedStream, options); screenChunks = []; screenRecorder.ondataavailable = (event) => { if (event.data.size > 0) screenChunks.push(event.data); }; screenRecorder.onstop = () => { // 释放流资源 micStream.getTracks().forEach(track => track.stop()); screenStream.getTracks().forEach(track => track.stop()); const resultBlob = new Blob(screenChunks, { type: 'video/webm' }); const url = URL.createObjectURL(resultBlob); // 自带下载逻辑可直接使用 chrome.downloads.download({ url: url, filename: `录制文件_${Date.now()}.webm` }); }; screenRecorder.start(); } catch (e) { console.error('录制初始化失败', e); } } else if (message.from === 'stop') { if (screenRecorder && screenRecorder.state !== 'inactive') { screenRecorder.stop(); } } }
4. 保留原popup.js逻辑即可
原有的popup按钮点击发消息的逻辑不需要修改,已经可以和新的background逻辑适配。
修改后所有媒体请求都在用户点击popup按钮的交互上下文触发,完全符合Chrome的权限规则,不会再触发对应错误。
内容的提问来源于stack exchange,提问作者GMAC
相关产品推荐
相关产品推荐

