MediaRecorder忽略VideoFrame.timestamp无法按指定帧率编码视频问题
核心原因
MediaRecorder 是面向实时流设计的录制API,默认以帧写入轨道的实际墙钟间隔计算帧率,不会直接采用VideoFrame上手动设置的timestamp、duration作为编码时间基准。哪怕你给帧打了10fps对应的时间戳,只要单帧生成+写入的实际间隔是1秒,最终输出的视频就会是1fps。
另外你贴的示例代码存在逻辑错误:视频帧时间戳必须严格单调递增,不能所有帧都用固定的(ms * 1) / fps值,第一帧时间戳应为0,后续每帧按帧时长累加。
可行方案
直接放弃MediaRecorder的实时录制链路,改用WebCodecs API自主完成编码,完全绕开实时时钟限制,不需要缓存全量帧,内存占用稳定:
- 初始化视频编码器,配置目标编码参数:
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, });
- 逐帧生成、逐帧编码,时间戳按目标帧率手动累加,和单帧生成耗时完全解耦:
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; }
- 编码完成后,调用封装器的flush方法生成最终视频Blob即可,整个流程生成一帧处理一帧,不需要预缓存所有帧。
附加问题解答
VLC无法识别总时长,是因为MediaRecorder录制实时流时无法提前预知流的结束时间,默认不会在文件头写入总时长元数据。
如果使用上述WebCodecs+自主封装的方案,初始化封装器时直接传入总帧数、帧率参数,封装器会自动把总时长写入文件元数据,所有播放器都能正常识别时长信息。如果坚持使用MediaRecorder,只能在录制完成后重写文件头补全元数据,流程复杂度更高。
方案适配说明
你提到的「缓存所有帧再按目标速度回放录制」的方案确实不适用长视频场景,WebCodecs方案逐帧处理、逐帧释放资源,内存占用不会随视频时长增长,稳定性远高于回放录制方案。
该方案兼容范围和你提供的复现Demo一致:Chrome 94+、Safari 16.4+版本可直接运行,Safari下只需要把编码配置切换为MP4兼容的H.264格式即可。
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

