基于 FFmpeg 与 rrweb-player,实现 linux 环境中的回放数据转视频
背景与需求
在视频回溯中,可视化回放是将JSON 数据转化为网页上的交互行为,若需要直观的看到之前录制的用户行为操作,需要在浏览器上进行操作展示,使用场景有限制。若将JSON 数据转化为MP4,则可以直观的将用户的实际操作可视化呈现出来,非常方便后续的分享、交流以及故障排查与错误重现分析,甚至进行核查取证。同时,与JSON 数据相比,MP4 更不易于被进行修改。
因此,将 JSON 数据转化成 MP4 视频具有便于分享和不易修改的优势。为了支持动态的将用户交互过程数据转视频并支持批量转换,我们需要在linux服务端基于rrweb-player、FFmpeg、无头浏览器 puppeteer 实现用户的交互过程数据转视频功能。
rrweb-player 介绍
rrweb-player 是一个基于 rrweb 项目的开源的播放库,专门用于解析 rrweb 录制的用户操作过程的 JSON 数据,将这些数据以 dom 的形式,在浏览器中根据时间顺序展示 dom 变化的增量视图,以便于对用户的操作过程数据进行可视化回放。但 rrweb-player只能帮助我们还原用户交互过程,本身并不能直接将回放数据转换为视频,若要将交互过程转换成对应的视频仍需要其他的工具辅助支持。
回放转视频的实现
为了将用户的交互操作数据转化为可视化的视频,如 .mp4 视频,我们可以通过视频帧的形式,将浏览器中的可视化用户交互回放页面,根据帧率按时间进行截屏,然后将每一张截屏内容作为每一帧的数据,通过 FFmpeg 插件将截取到的每一帧图片的 buffer 数据写入到视频文件中,最后生成完整的 MP4 视频文件。
用户交互数据转图片
通过 rrweb 对用户交互过程的录制,可以得到用户交互过程的时间与 dom 变化的 JSON 数据(events),rrweb-player 则能将用户交互过程的 JSON 数据,在浏览器上根据 timestamp 按顺序展示用户行为快照。
events 是指记录和追踪用户在网页上进行的各种交互行为数据,这些数据包含了用户在页面上的操作细节、以及与每个操作相关的时间信息。每一条操作数据都有 type、data、timestamp 属性。
type 是指EventType,默认情况第一条数据用来表示上下文,即4:Mata类型,第二条为全量数据,即 2:FullSnapshot类型,后续更多的便是增量数据类型,即 3:IncrementalSnapshot 类型,增量事件数据中包含了各种页面操作事件数据,如dom变更数据(mutationData)、鼠标数据(mouseInteractionData)、鼠标移动数据(mousemoveData)、滚动数据(scrollData)、表单事件(inputData)、canvas事件(canvasMutationData)等。
rrweb录制过程:
rrweb-player回放过程:
回放中通过解析events 数据获取到的时间进度:
在浏览器中将某一时刻的用户行为界面转化为一系列的图片可通过无头浏览器 puppeteer 截图实现。
回放是一个按照 events 中的 timestamp 制定定时器(内部使用了 requestAnimationFrame 处理,保证页面回放的流畅性),再根据 timestamp 对应的 event 中的事件或 dom 元素变化进行重建dom快照,构建出新的节点并添加到 iframe 容器中的过程。最终会形成一个带有时间进度的回放页面。
截屏方式有以下三种:
- 回放页面自动执行增量快照更新,puppeteer上根据在脚本上的时间向回放页面发起截屏。
此时在进行截屏过程中会存在脚本执行的时间差,导致时间跨度大,输出的视频帧比真实的要少,最终导致视频缺帧情况。
- 回放页面关闭自动更新,根据脚本的时间差使回放页面跳转到 events 某一时刻的页面,然后进行截屏。
回放的 event 中的数据除了dom变更之外还有一些js操作,比如 canvas 的内容绘制,此时若直接跳转到 events 的某一刻页面,将会跳过可能的js执行,将导致转换的视频内容可能存在空白或者与回放内容不匹配的情况。
- 回放页面关闭自动更新,根据脚本设置时间片段,按顺序对回放进行片段播放,再播放完成后通知puppeteer 进行截屏。
此方式能同时解决方式1、方式2的问题,方式3的处理也能够兼容处理截屏时的异常场景,避免丢帧,特别是在批处理转视频的过程中。
FFmpeg将图片转视频
FFmpeg 是一个开源的跨平台多媒体处理工具,能够处理音频、视频等多种格式的数据。将图片转换为视频是其中一个常见用途之一,将图片转换为视频的主要流程如下:
rust
初始化 FFmpeg 线程 --> 确定帧率和分辨率
↓
选择图像编码器
↓
构建视频流
↓
初始化视频流 --> 逐帧写入 --> 写入完毕
↓
生成视频文件
在初始化视频流时,FFmpeg 会将输出视频流的相关配置都输出
上图为 FFmpeg 在 linux 上将图片转换为 mp4 视频,初始化视频流时的配置信息。图中的 Stream 部分为视频流的信息,根据上面的流信息,可以看到如下信息:
- 输出视频的视频编解码器是H.264(AVC),编码标识符为"avc1";
- 像素格式是 yuv444p,逐行扫描(progressive)的电视制式(tv);
- 视频分辨率为264x576像素;
- 视频压缩质量范围为2-31;
- 视频帧率为每秒20帧;
如上方的默认配置,并不是所有的播放器和设备都对 yuv444p 的像素格式提供了完整的支持,可能会存在部分播放器无法播放的问题。考虑到转换后的视频的兼容性,在转换过程中可使用-pix_fmt yuv420p
将转换的像素格式配置为更加兼容、更加广泛、适用于大多数播放器和设备的 yuv420p 。
根据以上图片转视频的场景,我们需要在linux上通过脚本实时生成数据流,且输入格式为图片,因此我们需要通过使用 -f image2pipe
来指定输入格式的参数为image2pipe
,将图像数据通过管道(pipe)传递给 FFmpeg 进行编码、处理或转换。
同时,为了后续能够更好的将图片处理成视频,FFmpeg 需要知道从哪里读取图像数据,此时需要使用 -i -
来告诉 FFmpeg 从标准输入读取数据,即从管道中获取图像数据。一般情况下,image2pipe
和 -i -
通常需要同时使用,否则只使用image2pipe
将无法正常转换。
整体回放数据转视频的实现流程如下:
总结
将视频回溯录制的JSON 数据转化为MP4视频为视频回溯提供了一种更直观、易于分享和分析的方式。通过在Linux上的执行,我们能够实时生成图像数据流,并将其传递给 FFmpeg 进行处理,以生成最终的MP4视频文件。并能够支持数据转视频的批处理,为后续的可视化回放、数据分析和更广泛的应用领域提供了便利。