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

如何在HTTP multipart/x-mixed-replace的MJPEG流中计算FPS?

流式图片FPS计算方案:可行性、实现方法与工具推荐

可行性结论

完全可行。尽管浏览器通过单Content-Download响应流更新<img>标签时不会发起多请求,但可以通过监听图像渲染事件+时间戳追踪,精准计算每帧的渲染完成时间、帧间间隔,最终得到端到端链路的FPS指标。

第三方工具推荐

不需要依赖复杂的第三方库,原生API结合简单封装就能实现。如果追求开箱即用的方案,可考虑:

  • stats.js:轻量级性能监控工具,原本用于WebGL/Canvas的FPS统计,稍作改造就能适配流式图片场景,核心逻辑是基于requestAnimationFrame追踪渲染周期。
  • 原生Performance API:浏览器内置的性能接口,能提供高精度时间戳,是实现自定义统计的基础。

具体实现步骤

核心逻辑

通过<img>的load事件捕捉帧下载完成的节点,再用requestAnimationFrame确保获取到浏览器实际渲染完成的时间;结合后端传递的帧生成时间戳(若有)计算端到端延迟,通过连续帧的时间差计算帧间间隔与FPS。

代码示例

// 状态变量初始化
const frameIntervals = [];
const trackFrameCount = 30; // 保留最近30帧数据计算平均FPS
let lastRenderTimestamp = null;
let backendFrameTimestamps = []; // 存储后端推送的每帧生成时间戳(需后端配合)

const streamImg = document.getElementById('stream-image');
streamImg.addEventListener('load', () => {
  // 用requestAnimationFrame确保捕捉到渲染完成的准确时间
  requestAnimationFrame(() => {
    const currentTime = performance.now();
    
    if (lastRenderTimestamp) {
      // 计算当前帧与上一帧的间隔
      const interval = currentTime - lastRenderTimestamp;
      const realtimeFps = Math.round(1000 / interval);
      
      frameIntervals.push(interval);
      // 维护固定长度的帧数据队列
      if (frameIntervals.length > trackFrameCount) {
        frameIntervals.shift();
      }
      
      // 计算平均FPS
      const avgInterval = frameIntervals.reduce((sum, val) => sum + val, 0) / frameIntervals.length;
      const avgFps = Math.round(1000 / avgInterval);
      
      console.log(`帧间隔: ${interval}ms | 实时FPS: ${realtimeFps} | 平均FPS: ${avgFps}`);
      
      // 若后端推送了帧生成时间戳,计算端到端延迟
      if (backendFrameTimestamps.length) {
        const backendTime = backendFrameTimestamps.shift();
        const endToEndDelay = currentTime - backendTime;
        console.log(`端到端延迟: ${endToEndDelay}ms`);
      }
    }
    
    lastRenderTimestamp = currentTime;
  });
});

关键细节说明

  • requestAnimationFrame的必要性:img.onload仅标记图像下载完成,此时图像未必完成渲染,用该API可确保在浏览器下一次重绘后获取准确的渲染时间点。
  • 后端时间戳传递方式:若要计算端到端链路的真实FPS,需后端为每帧生成时间戳并传递,可通过两种方式实现:
    • 单独建立WebSocket通道,每生成一帧就推送对应的时间戳;
    • 采用多部分响应格式,在每帧图像前附带时间戳元数据。
  • 数据稳定性处理:仅保留最近N帧的间隔数据计算平均FPS,避免早期波动干扰结果。

注意事项

  • 后台标签页节流:当浏览器标签页处于后台时,渲染频率会被降低,此时计算的FPS会失真,可通过document.hidden判断标签页状态,忽略后台时段的数据。
  • 缓存规避:后端需设置Cache-Control: no-cache响应头,避免浏览器缓存旧帧导致统计错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:20:28