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

MediaRecorder忽略VideoFrame.timestamp无法按指定帧率编码视频问题

核心原因

MediaRecorder 是面向实时流设计的录制API,默认以帧写入轨道的实际墙钟间隔计算帧率,不会直接采用VideoFrame上手动设置的timestamp、duration作为编码时间基准。哪怕你给帧打了10fps对应的时间戳,只要单帧生成+写入的实际间隔是1秒,最终输出的视频就会是1fps。
另外你贴的示例代码存在逻辑错误:视频帧时间戳必须严格单调递增,不能所有帧都用固定的(ms * 1) / fps值,第一帧时间戳应为0,后续每帧按帧时长累加。

可行方案

直接放弃MediaRecorder的实时录制链路,改用WebCodecs API自主完成编码,完全绕开实时时钟限制,不需要缓存全量帧,内存占用稳定:

  1. 初始化视频编码器,配置目标编码参数:
const encoder = new VideoEncoder({
  output: (chunk) => {
    // 将编码输出的视频块送入对应格式的媒体封装器
    muxer.addVideoChunk(chunk);
  },
  error: console.error
});
encoder.configure({
  codec: 'vp09.00.10.08', // 输出webm用VP9编码,输出mp4可替换为H.264编码'avc1.42001E'
  width: canvas.width,
  height: canvas.height,
  framerate: 10,
});
  1. 逐帧生成、逐帧编码,时间戳按目标帧率手动累加,和单帧生成耗时完全解耦:
const MICROSECOND_PER_SEC = 1_000_000;
const frameDuration = MICROSECOND_PER_SEC / 10; // 10fps对应每帧时长100000微秒
let currentTimestamp = 0;
// 原有单帧渲染逻辑,不管单帧渲染耗时多久都不影响最终帧率
for (let i = 0; i < totalFrameCount; i++) {
  await renderContentToCanvas(canvas); // 你的单帧绘制逻辑
  const frame = new VideoFrame(canvas, {
    timestamp: currentTimestamp,
    duration: frameDuration
  });
  encoder.encode(frame);
  frame.close(); // 及时释放VideoFrame内存避免泄漏
  currentTimestamp += frameDuration;
}
  1. 编码完成后,调用封装器的flush方法生成最终视频Blob即可,整个流程生成一帧处理一帧,不需要预缓存所有帧。

附加问题解答

VLC无法识别总时长,是因为MediaRecorder录制实时流时无法提前预知流的结束时间,默认不会在文件头写入总时长元数据。
如果使用上述WebCodecs+自主封装的方案,初始化封装器时直接传入总帧数、帧率参数,封装器会自动把总时长写入文件元数据,所有播放器都能正常识别时长信息。如果坚持使用MediaRecorder,只能在录制完成后重写文件头补全元数据,流程复杂度更高。

方案适配说明

你提到的「缓存所有帧再按目标速度回放录制」的方案确实不适用长视频场景,WebCodecs方案逐帧处理、逐帧释放资源,内存占用不会随视频时长增长,稳定性远高于回放录制方案。
该方案兼容范围和你提供的复现Demo一致:Chrome 94+、Safari 16.4+版本可直接运行,Safari下只需要把编码配置切换为MP4兼容的H.264格式即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 13:36:22