关于前端实现录制功能

本文可以说是一篇调研学习文,是我自己感觉可行的一套方案,后续会去读读已经开源的一些类似的代码库,补足自己遗漏的一些细节,所以大家可以当作学习文,生产环境慎用。

录屏重现错误场景

如果你的应用有接入到web apm系统中,那么你可能就知道apm系统能帮你捕获到页面发生的未捕获错误,给出错误栈,帮助你定位到BUG。但是,有些时候,当你不知道用户的具体操作时,是没有办法重现这个错误的,这时候,如果有操作录屏,你就可以清楚地了解到用户的操作路径,从而复现这个BUG并且修复。(本次先录制,下次再回放)

一、先来看看音频可视化的实现思路:

思路

需要将音频文件解码成二进制流文件,这个流文件即为音频资源buffer播放源,之后需要将buffer播放源与分析器相连,分析器与扬声器相连;
audioContext.destination:返回AudioDestinationNode对象,表示当前audio context中所有节点的最终节点,一般表示音频渲染设备。

二、录制视频的实现

简单了解了音频可视化以后咱们就来研究一下录屏的实现。

关于录屏和摄像对比了两种方法:

  • 使用HTML5的api实现
    摄像:mediaDevices(获取设备)+ getUserMedia(获取流) + MediaRecorder(存储)
    录屏:getDisplayMedia(获取流) + MediaRecorder(存储)
  • ffmpeg + node:FFmpeg是一套非常强大的音视频处理的开源工具,不多介绍,而Electron基于node和chromium,它允许使用node的API以及几乎所有的node模块,这意味这着我们可以调用cmd命令来操作ffmpeg实现录屏和摄像录制,当然ffmpeg功能绝不止这点。
HTML5实现

mediaDevices

  • 用于收集系统上可用的多媒体输入和输出设备的信息
  • 该方法调用成功返回设备列表,并传入带有devceID的MediaStreamConstraints对象可以指定设备获取流媒体来源
  navigator.mediaDevices.enumerateDevices().then(devicelist => {
    // audiooutput 扬声器
    // audioinput  麦克风
    // audiooutput 摄像
    console.log(devicelist)
  }).catch(err => console.log(err))

getUserMedia

  • 用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。
  • 该方法返回视频流,将获取到的流赋给video标签可实现边录边看
navigator.mediaDevices.getUserMedia(MediaStreamConstraints).then(stream => {
    videoElement.srcObject = stream; // 
  }, error => console.log(error));

MediaRecorder

  • 记录和捕获媒体,也就是视频和音频
  • getDisplayMedia 和 getUserMedia 获取到的流都需要使用MediaRecorder存储起来,并且可以保存成文件
let herf
this.recorder = new MediaRecorder(stream);
this.recorder.ondataavailable = e => { 
  herf = e.data;
  download.href = URL.createObjectURL(herf);
};
this.recorder.start();

以上是我在网上找了找HTML5实现的一些方法,希望能给大家带来一点思路。

使用ffmpeg

一些基本参数:

  • -formats 输出所有可用格式
  • -f fmt 指定格式(音频或视频格式)
  • -i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头
  • -y 覆盖已有文件
  • -t duration 记录时长为t
  • -fs limit_size 设置文件大小上限
  • -itsoffset time_off 设置时间偏移(s),该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset秒。
  • [-]hh:mm:ss* [.xxx]的格式也支持 音 频
  • -ab bitrate 设置音频码率
  • -ar freq 设置音频采样率
  • -ac channels 设置通道 缺省为1 视 频
  • -b bitrate 设置比特率,缺省200kb/s
  • -r fps 设置帧频 缺省25
  • -s size 设置帧大小 格式为WXH 缺省160X128.下面的简写也可以直接使用:

录屏相关命令

列出可用的设备包括音频和摄像等等
ffmpeg -list_devices true -f dshow -i dummy
 
录屏,你也可以加入关于视频的一些基本参数来获得你想要的文件
ffmpeg -f gdigrab -i desktop captrue.mkv -y

node调用

d进入bin文件夹后执行录屏相关命令
关于停止录制,虽然ffmpeg按 Q 可以停止录制,但是我们通过代码调用是看不到cmd命令行的而且他在录制过程中是一直占用这个进程什么命令也无法输入 所以这个地方我只想到一个办法就是强制停止该进程

这个属于是一个录制视频的组件库,基本方法就是调用一些特定的方法和参数就可以实现。

接下来是我最近看的一篇文章看到的一些思路,也写给大家参考一下

思路一:利用Canvas截图
这个思路比较简单,就是利用canvas去画网页内容,比较有名的库有: html2canvas ,这个库的简单原理是:

  1. 收集所有的DOM,存入一个queue中;
  2. 根据zIndex按照顺序将DOM一个个通过一定规则,把DOM和其CSS样式一起画到Canvas上。

这个实现是比较复杂的,但是我们可以直接使用,所以我们可以获取到我们想要的网页截图。为了使得生成的视频较为流畅,我们一秒中需要生成大约25帧,也就是需要25张截图,思路流程图如下:
借鉴别人的图片
但是,这个思路有个最致命的不足:为了视频流畅,一秒中我们需要25张图,一张图300KB,当我们需要30秒的视频时,图的大小总共为220M,这么大的网络开销明显不行。

思路二:记录所有操作重现

为了降低网络开销,我们换个思路,我们在最开始的页面基础上,记录下一步步操作,在我们需要"播放"的时候,按照顺序应用这些操作,这样我们就能看到页面的变化了。这个思路把鼠标操作和DOM变化分开:

鼠标变化:

  1. 监听mouseover事件,记录鼠标的clientX和clientY。
  2. 重放的时候使用js画出一个假的鼠标,根据坐标记录来更改"鼠标"的位置。

DOM变化:

  1. 对页面DOM进行一次全量快照。包括样式的收集、JS脚本去除,并通过一定的规则给当前的每个DOM元素标记一个id。
  2. 监听所有可能对界面产生影响的事件,例如各类鼠标事件、输入事件、滚动事件、缩放事件等等,每个事件都记录参数和目标元素,目标元素可以是刚才记录的id,这样的每一次变化事件可以记录为一次增量的快照。
  3. 将一定量的快照发送给后端。
  4. 在后台根据快照和操作链进行播放。

当然这个说明是比较简略的,鼠标的记录比较简单,我们不展开讲,主要说明一下DOM监控的实现思路。

思路:

首先你可能会想到,要实现页面全量快照,可以直接使用 outerHTML

const content = document.documentElement.outerHTML;

这样就简单记录了页面的所有DOM,你只需要首先给DOM增加标记id,然后得到outerHTML,然后去除JS脚本。
但是,这里有个问题,使用 outerHTML 记录的DOM会将把临近的两个TextNode合并为一个节点,而我们后续监控DOM变化时会使用 MutationObserver ,此时你需要大量的处理来兼容这种TextNode的合并,不然你在还原操作的时候无法定位到操作的目标节点。
那么,我们有办法保持页面DOM的原有结构吗?
答案是肯定的,在这里我们使用Virtual DOM来记录DOM结构,把documentElement变成Virtual DOM,记录下来,后面还原的时候重新生成DOM即可。

DOM转化为Virtual DOM

这里只需要关心两种Node类型: Node.TEXT_NODE 和 Node.ELEMENT_NODE 。 同时,要注意,SVG和SVG子元素的创建需要使用API: createElementNS,所以,我们在记录Virtual DOM的时候,需要注意namespace的记录

我们可以将整个documentElement转化为Virtual DOM,其中__flow用来记录一些参数,包括标记ID等,Virtual Node记录了: type、attributes、children、namespace。

Virtual DOM还原为DOM

将Virtual DOM还原为DOM的时候就比较简单了,只需要递归创建DOM即可,其中nodeFilter是为了过滤script元素,因为我们不需要JS脚本的执行。

以上就是实现一个录制的过程和一些方法,希望能给你带来一点点灵感

接下来我会研究一些录制完成后实现录制回放的功能,后续也写出来供大家参考。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端React实现录音并通过socket传输给后端的过程如下: 首先,前端需要引入相关的库和组件来实现录音功能。可以使用第三方库如Recorder.js,该库提供了录制音频的功能,并且支持转换为WAV格式。 在React组件中,可以在需要录音的地方添加一个录音按钮,并设置事件处理函数。在点击录音按钮时,调用录音相关代码,开始录制音频。 录制音频时,可以将音频数据通过Websocket进行传输。Websocket协议是一种双向通信协议,可以在浏览器和服务器之间建立持久的连接,实现实时数据传输。 在React中,可以使用第三方库如socket.io来实现Websocket的连接和数据传输。在录制音频时,将音频数据实时发送给后端服务器。 后端服务器接收到音频数据后,可以进行处理和保存。可以使用Node.js来创建一个服务器端应用,接收来自前端的音频数据,并进行相应的处理操作(如存储到数据库或进行音频处理等)。 在前端和后端之间建立的Websocket连接可以实现双向通信,因此后端也可以通过Websocket向前端发送一些消息或结果,前端可以根据接收到的消息进行相应的展示或处理。 总结来说,前端React实现录音功能,需要使用录音库和组件,通过点击按钮触发录音操作,并将音频数据通过Websocket传输给后端服务器进行处理和保存。通过Websocket建立的连接可以实现双向通信,并支持后端向前端发送消息或结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值