如何基于HLS/MPEG-DASH实现类Loom的浏览器长时录屏功能
实现类Loom的长时长浏览器/Chrome扩展录屏方案
一、核心思路(针对5小时+录制需求)
长时长录制绝对不能把整个视频存在浏览器内存里,直接会崩,所以必须走分片录制+边录边传的路子——和你推测的Loom逻辑一致:
- 用浏览器的MediaRecorder API按固定时长(比如10-30秒)切割视频片段,每生成一个分片就立刻上传到服务器,彻底避免内存溢出;
- 关于HLS/DASH:如果选HLS,服务器端要把上传的分片转成.ts格式并生成.m3u8索引文件;如果是DASH(对应你问的MPD文件),MPD的生成更适合放在服务器端做,浏览器JS基本不直接生成MPD,因为它要处理分片时长、码率、时序这些复杂元数据,服务器用工具生成更靠谱。
二、浏览器端录屏核心代码示例
1. 获取屏幕+音频流
先把屏幕流和麦克风流合并(如果需要录声音):
async function getCombinedStream() { const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: 'monitor' }, audio: true }); const micStream = await navigator.mediaDevices.getUserMedia({ audio: true }); // 合并音视频轨道 return new MediaStream([ ...screenStream.getVideoTracks(), ...micStream.getAudioTracks() ]); }
2. 分片录制
配置timeslice参数指定分片间隔,每到时间就触发分片生成:
const stream = await getCombinedStream(); // 用VP9编码,比H.264省一半空间,适合长录制 const recorder = new MediaRecorder(stream, { mimeType: 'video/webm; codecs=vp9' }); const recordingId = self.crypto.randomUUID(); // 生成唯一标识,区分不同录制任务 recorder.ondataavailable = (e) => { if (e.data.size === 0) return; // 立刻上传分片,带上录制ID和时间戳作为分片标识 uploadChunk(e.data, recordingId, Date.now()); }; // 每30秒生成一个分片 recorder.start(30000);
3. 分片上传
用FormData把分片传给服务器,注意带上唯一录制ID,方便服务器后续拼接:
async function uploadChunk(chunk, recordingId, timestamp) { const formData = new FormData(); formData.append('chunk', chunk, `${recordingId}_${timestamp}.webm`); formData.append('recordingId', recordingId); try { await fetch('/your-upload-endpoint', { method: 'POST', body: formData }); } catch (err) { // 上传失败存到本地,后续重试 localStorage.setItem(`failed-chunk-${recordingId}_${timestamp}`, chunk); } }
三、Loom的实际实现逻辑(基于反向工程分析)
Loom确实是客户端分片录制+实时上传的路子,还加了这些优化:
- 客户端会对分片做轻量编码优化,优先用VP8/VP9减少体积;
- 服务器接收分片后,会后台完成转码、合并,生成HLS的.m3u8或DASH的.mpd索引文件,方便后续流畅播放;
- 做了断点续传:客户端会记录已上传的分片ID,万一录制中断,重启后可以从断点继续上传,不用重录整个视频。
四、关于MPD文件的生成
浏览器JS几乎不会直接生成MPD,因为MPD需要处理多码率适配、分片时序、分辨率这些复杂逻辑,建议在服务器端用FFmpeg完成:
当所有分片上传完成后,服务器先把分片合并成完整视频,再转成DASH格式生成MPD,比如用这条FFmpeg命令:
ffmpeg -i combined.webm -c:v libx264 -c:a aac -f dash output.mpd
五、关键注意事项
- 内存清理:上传完分片后,要及时从内存中移除对应的Blob对象,避免长时间录制导致内存泄漏;
- Chrome扩展权限:如果做扩展,必须在
manifest.json里声明desktopCapture、audioCapture权限,还要加host_permissions允许访问你的上传服务器; - 错误重试:上传失败的分片要存在本地IndexedDB或localStorage,录制结束后统一重试;
- 编码选择:优先用VP9/AV1编码,带宽和存储成本比H.264低很多,适合5小时以上的长视频。
内容的提问来源于stack exchange,提问作者kai gan
相关产品推荐
相关产品推荐

