You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome扩展Manifest v3迁移:Service Worker无MediaRecorder替代方案求助

解决方案

核心思路

将录制逻辑拆分到三个独立模块,解决Manifest v3下Service Worker无DOM、页面刷新导致录制中断的问题:

  • Service Worker(后台):负责定时调用chrome.tabs.captureVisibleTab捕获页面截图,监听页面状态变化,传递截图数据到持久化录制上下文。
  • 持久化录制上下文(侧边栏):承载canvas和MediaRecorder实例,接收后台传来的截图并绘制到canvas,持续录制流——该上下文独立于目标页面生命周期,不会随页面刷新销毁。
  • 内容脚本(可选):若需捕获页面内音频或交互事件,可在此处理后传递到录制上下文。

具体实现步骤

1. 配置Manifest v3权限

确保manifest.json包含所需权限与配置:

{
  "manifest_version": 3,
  "name": "网页会话录制工具",
  "version": "1.0",
  "permissions": ["tabs", "activeTab", "sidePanel"],
  "background": {
    "service_worker": "background.js"
  },
  "side_panel": {
    "default_path": "sidepanel.html"
  },
  "action": {
    "default_title": "打开录制侧边栏"
  }
}

2. 后台Service Worker(background.js)

负责截图捕获与状态管理:

// 捕获帧率设置(30帧/秒)
const CAPTURE_INTERVAL = 1000 / 30;
let captureTimer = null;
let targetTabId = null;

// 点击扩展图标打开侧边栏并记录目标标签页
chrome.action.onClicked.addListener(async (tab) => {
  targetTabId = tab.id;
  await chrome.sidePanel.open({ tabId: tab.id });
});

// 接收侧边栏的录制控制指令
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'START_RECORDING') {
    startCapture();
    sendResponse({ status: 'started' });
  } else if (message.type === 'STOP_RECORDING') {
    stopCapture();
    sendResponse({ status: 'stopped' });
  }
});

// 启动定时截图捕获
function startCapture() {
  if (captureTimer) return;
  captureTimer = setInterval(async () => {
    if (!targetTabId) return;
    try {
      // 直接捕获为Blob,避免base64的性能损耗
      const blob = await chrome.tabs.captureVisibleTab(null, { format: 'png' });
      const arrayBuffer = await blob.arrayBuffer();
      // 传递截图数据到侧边栏
      chrome.runtime.sendMessage({
        type: 'NEW_SCREENSHOT',
        data: arrayBuffer,
        tabId: targetTabId
      });
    } catch (err) {
      console.error('截图捕获失败:', err);
    }
  }, CAPTURE_INTERVAL);
}

// 停止截图捕获
function stopCapture() {
  if (captureTimer) {
    clearInterval(captureTimer);
    captureTimer = null;
  }
}

// 监听页面刷新状态,避免录制空白帧
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
  if (tabId === targetTabId && changeInfo.status === 'loading') {
    clearInterval(captureTimer);
    captureTimer = null;
  } else if (tabId === targetTabId && changeInfo.status === 'complete') {
    if (!captureTimer) startCapture();
  }
});

3. 侧边栏页面(sidepanel.html)

提供录制控制UI与持久化上下文:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 300px; padding: 10px; }
    button { width: 100%; padding: 8px; margin: 5px 0; }
    #status { margin: 10px 0; }
  </style>
</head>
<body>
  <button id="startBtn">开始录制</button>
  <button id="stopBtn">停止录制</button>
  <div id="status">未录制</div>
  <script src="sidepanel.js"></script>
</body>
</html>

4. 侧边栏脚本(sidepanel.js)

处理截图绘制与MediaRecorder录制逻辑:

let canvas = null;
let ctx = null;
let mediaRecorder = null;
let recordedChunks = [];

// 初始化录制用canvas
function initCanvas() {
  canvas = document.createElement('canvas');
  ctx = canvas.getContext('2d');
  // 按目标页面分辨率设置canvas尺寸(可根据实际调整)
  canvas.width = 1920;
  canvas.height = 1080;
}

// 开始录制按钮逻辑
document.getElementById('startBtn').addEventListener('click', async () => {
  initCanvas();
  // 从canvas获取可录制的MediaStream
  const stream = canvas.captureStream(30);
  mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
  recordedChunks = [];

  // 收集录制数据
  mediaRecorder.ondataavailable = (e) => {
    if (e.data.size > 0) recordedChunks.push(e.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 = `会话录制-${Date.now()}.webm`;
    a.click();
    URL.revokeObjectURL(url);
    document.getElementById('status').textContent = '录制已停止,视频已下载';
  };

  mediaRecorder.start();
  document.getElementById('status').textContent = '正在录制...';
  // 通知后台启动截图捕获
  await chrome.runtime.sendMessage({ type: 'START_RECORDING' });
});

// 停止录制按钮逻辑
document.getElementById('stopBtn').addEventListener('click', async () => {
  if (mediaRecorder && mediaRecorder.state !== 'inactive') {
    mediaRecorder.stop();
    await chrome.runtime.sendMessage({ type: 'STOP_RECORDING' });
  }
});

// 接收后台传来的截图并绘制到canvas
chrome.runtime.onMessage.addListener(async (message) => {
  if (message.type === 'NEW_SCREENSHOT' && canvas) {
    const blob = new Blob([message.data], { type: 'image/png' });
    const imgBitmap = await createImageBitmap(blob);
    ctx.drawImage(imgBitmap, 0, 0, canvas.width, canvas.height);
    imgBitmap.close();
  }
});

关键优化点

  • 避免Base64传递:直接用Blob/ArrayBuffer传输截图,大幅提升效率、降低延迟。
  • 持久化录制上下文:侧边栏独立于目标页面,页面刷新时录制不会中断,彻底解决分轨问题。
  • 页面状态监听:页面刷新时暂停捕获,加载完成后恢复,避免录制空白帧。
  • 高效绘制:使用createImageBitmap处理截图,比传统Image对象加载更快,适配高帧率录制需求。

内容的提问来源于stack exchange,提问作者Avinash Dixit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:50:20