背景说明:
最近有在看GitHub上的rrweb项目,确实是一款DOM录制的神器,在使用文档中提供了很多我们会用到的场景和对应的示例,我们今天来看一下其中一个场景《转换为视频》,虽然rrweb直接回放的效果最佳但还是会遇到需要转为视频进行存储的要求,通过查看rrweb提供的rrvideo项目后决定写一下整个转换的过程,大致的流程图如下:
环境配置:
- 安装FFmpeg:用于将逐帧的图片数据转换为视频。
- 安装puppeteer:用于在后台加载网页。
- 安装rrweb-player:用于播放rrweb录制的events数据。
使用puppeteer打开空白页面:
- 获取browser对象实例:
browser = await puppeteer.launch({ headless: true });
; - 打开新页签:
page = await browser.newPage();
&await page.goto("about:blank");
; - 通过
page.exposeFunction
在window对象上挂载开始和结束录制的调用函数; - 将需要播放的events数据使用
page.setContent()
加载进页面。
try { browser = await puppeteer.launch({ headless: true }); page = await browser.newPage(); await page.goto("about:blank"); // 扩展启动录制函数 await page.exposeFunction("onReplayStart", async () => { await startReplay(); }); // 扩展结束录制函数 await page.exposeFunction("onReplayFinish", async () => { await finishReplay(); }); // 读取原数据 const events = JSON.parse( fs.readFileSync(path.resolve(process.cwd(), _input), "utf-8") ); await page.setContent(getHtml(events)); } catch (error) { console.log("openPage:", error); } 复制代码
组装最简支持rrweb-player播放的DOM结构:
- 获取安装到node_modules内的rrweb-player包的内容,便于插入到DOM中;
// 获取rrweb-player的脚本插入到DOM中 const rrwebScriptPath = path.resolve( require.resolve("rrweb-player"), "../../dist/index.js" ); const rrwebStylePath = path.resolve(rrwebScriptPath, "../style.css"); const rrwebRaw = fs.readFileSync(rrwebScriptPath, "utf-8"); const rrwebStyle = fs.readFileSync(rrwebStylePath, "utf-8"); 复制代码
- 拼装满足rrweb-player播放的基础DOM,其中在replayer.play()函数执行前开启录制并在监听到播放完成后结束录制:
const html = ` <html> <head> <style>${rrwebStyle}</style> </head> <body> <script> ${rrwebRaw}; /*<!--*/ const events = ${JSON.stringify(events).replace( /<\/script>/g, "<\\/script>" )}; /*-->*/ window.replayer = new rrwebPlayer({ target: document.body, props: { events, showController: false, }, }); window.onReplayStart(); window.replayer.play(); window.replayer.addEventListener('finish', () => { window.onReplayFinish() }); </script> </body> </html> `; 复制代码
通过puppeteer提供的screenshot函数定时截屏获取数据流:
- 获取到需要录制的元素对象:
const wrapperEl = await page.$(".replayer-wrapper");
- 通过screenshot来截取当前帧的画面,返回数据类型为二进制数据。
const buffer = await wrapperEl?.screenshot({ encoding: "binary", }); 复制代码
执行ffmpeg命令并将截屏数据输入到ffmpeg进程:
- 我们使用NodeJs提供的spawn函数来执行FFmpeg命令,此处未配置环境变量而直接引用的FFmpeg的绝对路径:
const ffmpegProcess = spawn("D:\\ffmpeg\\bin\\ffmpeg", [ // fps "-framerate", "15", // input "-f", "image2pipe", "-i", "-", // output "-y", _output, ]); 复制代码
- 将截图得到的二进制数据写入ffmpegProcess进程的标准输入流中:
ffmpegProcess.stdin.write(buffer);
总结说明:
- 以上就是对rrvideo流程拆解一些关键点说明,完整代码在GitHub。
- rrvideo还提供了常用的一些配置项来便于调整视频的尺寸等信息。
- puppeteer是继上次做自动生成骨架屏后的第二次使用。