<video>标签不能由Web Worker直接渲染或通信,但可协同实现实时画面分析:主线程负责视频流获取、帧捕获与像素数据零拷贝传递,Worker仅处理Uint8ClampedArray像素数据,执行统计、检测、压缩等纯计算任务,结果回传主线程用于渲染或业务逻辑。

<video> 标签本身不能直接和 Web Worker 通信或交由 Worker 渲染,但可以和 Worker 协同完成实时画面分析:主线程用 <video> 播放流、提取帧,Worker 在后台做纯计算(如人脸检测、运动分析、颜色统计等),不碰 DOM 和渲染。关键不是“让 Worker 处理 video”,而是“让 Worker 处理 video 的像素”。
主线程:负责视频流获取、帧捕获与数据传递
- 调用
navigator.mediaDevices.getUserMedia({ video: true })获取摄像头流 - 将流赋给
<video id="input-video" autoplay muted></video>(必须播放,否则帧不更新) - 使用
requestVideoFrameCallback()(推荐)监听每一帧就绪时机,比setInterval + drawImage更精准、低延迟 - 帧就绪后,用离屏
OffscreenCanvas(或隐藏<canvas>)调用drawImage(video, 0, 0),再执行getContext('2d').getImageData(0, 0, w, h)得到Uint8ClampedArray像素数据 - 连同宽高、时间戳一起,通过
worker.postMessage(data, [data.data.buffer])零拷贝传入 Worker(注意传data.data.buffer,不是整个ImageData对象)
⚠️ 不要传
canvas、video、ImageData实例本身——它们不可序列化,Worker 无法接收。
Web Worker:只做像素级分析,不操作任何 DOM
Worker 收到的是原始 RGBA 像素数组(每 4 字节为 R-G-B-A),可安全执行:
- 区域统计:遍历像素算平均亮度、色相分布、运动区块(前后帧差分)
- 简单检测逻辑:比如绿幕区域占比、人脸大致位置(用肤色 HSV 阈值粗筛)
- 特征压缩:把 1280×720 像素降采样为 64×36 的灰度直方图,只传回轻量特征向量
-
预处理输出:如标记出疑似人脸的矩形坐标
[x, y, w, h],或生成二值掩码Uint8Array
✅ 所有操作都在
Uint8ClampedArray上进行,不调用canvas.getContext,不创建新ImageBitmap(除非你确认浏览器支持createImageBitmapin Worker)立即学习“前端免费学习笔记(深入)”;
分析结果回传与响应闭环
- Worker 处理完,将结果(如
{ faces: [[120,80,60,60]], avgHue: 125 }或maskBuffer)发回主线程 - 主线程收到后:
- 可在
<canvas>上用putImageData()或fillRect()叠加分析框(适合调试) - 更优做法是:把分析结果交给业务逻辑(如触发告警、调节滤镜参数、驱动动画)
- 若需持续反馈,可结合
requestIdleCallback控制分析频率,避免 Worker 过载
- 可在
性能要点:稳住 20–30 fps 的真实前提
-
帧率节流:不是每帧都送 Worker,例如
if (performance.now() - lastAnalyzeTime > 50)再处理(即 ≈20fps) -
对象复用:Worker 中不要
new Uint8ClampedArray(w*h*4),而是复用传入的 buffer 视图(如new Uint8ClampedArray(e.data.buffer)) - 避免主线程阻塞:所有耗时计算(哪怕只是遍历 10 万像素)都留在 Worker,主线程只做采集+渲染+调度
-
降分辨率预处理:在主线程用
OffscreenCanvas.transferToImageBitmap().scale()先缩放到 320×240 再传,大幅降低 Worker 计算压力
不复杂但容易忽略



















