Web Worker 不能直接操作视频或 Canvas,但可通过零拷贝传递像素数组分担帧级计算:主线程负责采集、解码、绘制与渲染,Worker 专注缩放、格式转换、合成与滤镜等纯计算任务,处理后回传结果由主线程闭环渲染。

Web Worker 不能直接操作视频或 Canvas,但它能高效分担帧级计算任务——主线程负责采集、解码、绘制和渲染,Worker 专注像素数组的纯计算处理,两者通过零拷贝消息协同,既保帧率又不卡 UI。
主线程管“采”和“画”
所有涉及 DOM、媒体流和渲染的操作必须由主线程完成:
- 调用 getUserMedia() 获取摄像头或屏幕流,或接收 WebRTC 远程流
- 将 MediaStream 赋给 <video> 元素(即使隐藏也要播放,否则帧不更新)
- 用 requestVideoFrameCallback(推荐)或定时
captureStream().getVideoTracks()[0].requestFrame()控制抽帧节奏 - 借助 OffscreenCanvas(最优)或隐藏
<canvas>调用drawImage(video, ...),再用getImageData()提取 RGBA 或 YUV 像素数据 - 通过
postMessage(data, [data.buffer])零拷贝传给 Worker——关键在传递Uint8ClampedArray.buffer,避免深拷贝开销
Worker 管“算”,只碰像素不碰 DOM
Worker 收到的是已解码的原始像素数组(如 1280×720×4 的 Uint8ClampedArray),它可安全执行以下无副作用的计算:
- 缩放与裁剪:比如双线性缩放到 320×180,或截取左上角 200×150 区域用于画中画
- 格式转换:YUV420 → RGBA(若主线程传入的是 YUV 帧)、RGBA → grayscale
- 叠加合成:多路帧按坐标+alpha 逐像素混合(如贴纸、水印)
- 轻量滤镜:遍历像素修改 R/G/B/A 值,不依赖 canvas API
-
生成 Blob:调用
OffscreenCanvas.convertToBlob()输出 JPEG,或手动编码为 WebP
注意:Worker 无法访问 <canvas>、<video>、document 或 window,也不能调用 getContext() 或 drawImage()。
回传结果并闭环渲染
Worker 处理完成后,把结果发回主线程,由主线程完成最终呈现:
- 若返回的是新尺寸的像素数组(如缩放后的
Uint8ClampedArray),主线程用putImageData()绘制到预览<canvas> - 若返回的是
Blob,调用URL.createObjectURL(blob)生成临时 URL,设为<img>的src,或用drawImage()绘制到 canvas - 每次使用完
createObjectURL()后,**必须立即调用revokeObjectURL()**,否则内存持续增长 - 主线程应主动节流,例如限制每 200ms 最多处理一帧,防止 Worker 积压任务或帧堆积
性能关键:零拷贝 + 对象复用 + 节流
要达到 ≥25fps 的实时性,需同时守住三个底线:
-
零拷贝传输:始终用
postMessage(data, [buffer])传递像素 buffer,而非整个 ImageData 对象 - 对象复用:主线程复用同一块 OffscreenCanvas 和 ImageData 实例,避免频繁分配内存;Worker 中也复用输出数组
-
帧率节流:根据 CPU 负载动态调整抽帧间隔,可用
performance.now()监控单帧处理耗时,超限时跳过下一帧
不复杂但容易忽略。


















