暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

关于车联网的v2x平台的前端录屏实现方案

903

1. 需求背景

近几年随着视频领域的不断发展,市场的蛋糕越来越大,为了更好的满足市场需求也催生了各个细分领域。

        从技术层面来看,国内现在已经走向了音视频标准的制定参与者,包括AVS标准、HDR标准等。从市场应用层面来看,主流媒体成为音视频专业级赛道产品性能的试金石,例如央视的4K频道,移动互联网借助短视频来连接用户和品牌,并成了品牌营销的极佳窗口,物联网行业借助视频以及5G技术让远距离控制变成了现实。

        布尔科技基于市场需求以及自研的V2X运控平台(物联网), 对 ”录屏“ 领域做了深层次的研究;最终,基于 浏览器内置API 实现了“前端录屏功能”并限制录制时长,该功能可兼容当前市场主流浏览器。


2. 核心技术

开发过程中使用到的核心技术如下:

(1) 浏览器内置的API navigator.mediaDevices.getDisplayMedia:提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口)在一个媒体流里
(2) MediaRecorder构造函数:可以创建一个 对指定的'媒体流'进行录制 的MediaRecorder对象
(3) MediaRecorder.ondataavailable方法:处理dataavailable事件,便于后面把视频数据转换成Blob对象
(4) Blob构造函数:可以把其他非blob对象和数据构造成一个Blob对象
(5) 第三方插件 fix-webm-metainfo:用于解决录屏后进度条异常的Bug
(6) window.URL.createObjectURL:浏览器内置方法,用于将Blob对象转换成可以下载的链接



3. 实现过程

说明: 该功能基于 vue 及 element-UI组件库进行工程化开发,该功能用到了一个第三方插件,需要提前搭建好环境并安装第三方插件 fixWebmMetaInfo

(1) 创建录屏按钮和结束录屏按钮,绑定点击事件

(2) 核心代码

<script>
import fixWebmMetaInfo from 'fix-webm-metainfo';
   export default {
       ...
     data() {
       return {
           timer: null,
           isRecord: false,
           allChunks: [],
      };
    },
     destroyed: function() {
       // 切换组件的时候如果没有停止录制,那么在这里调用结束录制的方法
       if (this.isRecord) {
           this.stopRecord();
      }
    },
     methods: {
       // 开始录屏
       async record() {
           if (this.isRecord) return this.$message.warning('请先结束录制!');
           try {
               captureStream = await navigator.mediaDevices.getDisplayMedia({
                   video: true,
                   audio: true, // not support
                   cursor: 'always'
              });
               this.$message.success('开始录屏');
               this.isRecord = true;
          } catch (e) {
               if (e === 'NotAllowedError: Permission denied') {
                   this.$message.error('您的浏览器不支持录屏');
              } else {
                   this.$message.warning('已取消录制');
              }
               return;
          }

           // new 一个媒体记录
           recorder = new MediaRecorder(captureStream, {
               audioBitsPerSecond: 128000,
               videoBitsPerSecond: 2500000,
               // webm类型一定要加codecs=vp8,opus,否则会导致录制时候时而可以用时而不能用
               mimeType: 'video/webm;codecs=vp8,opus'
          });

           // 重置数组,否则每次下载的视频都是第一回录制的视频
           this.allChunks.length = 0;
           // 给参数设置一个毫秒值,录制的媒体会按照你设置的值进行分割成一个个单独的区块, 而不是以默认的方式录制一个非常大的整块内容.
           recorder.start(10);

           // 使用定时器设置录制时间最长为10分钟,超过十分钟自动调结束录屏方法
           this.timer = setTimeout(() => {
               this.stopRecord();
          }, 600000);

           // 点击了浏览器提供的结束录制按钮时触发的回调函数 => 不绑定会报错
           captureStream.getVideoTracks()[0].onended = () => {
               this.stopRecord();
          };

           // 处理dataavailable事件,便于后面把视频数据转换成Blob对象
           recorder.ondataavailable = (e) => {
               this.allChunks.push(
                   // 将数据添加到视频列表中
                   e.data
              );
          };
      },
       // 录屏结束
       stopRecord() {
           if (!this.isRecord) return this.$message.warning('请先开始录制!');
           this.$message.success('录制结束');
           recorder.stop();
           // 清除定时器
           clearTimeout(this.timer);
           this.isRecord = false;
           let tracks = captureStream.getTracks();
           tracks.forEach((track) => track.stop());
           // 录屏结束以后自动下载
           this.download();
      },
       // 下载视频
       async download() {
           const link = document.createElement('a');
           link.style.display = 'none';
           const oldBlob = new Blob(this.allChunks, { 'type': recorder.mimeType });
           // 使用第三方插件转换blob文件 => 解决进度条长度异常的bug
           const newBlob = await fixWebmMetaInfo(oldBlob);
           const downloadUrl = window.URL.createObjectURL(newBlob);
           link.href = downloadUrl;
           link.download = '视频录制.webm';
           document.body.appendChild(link);
           link.click();
           link.remove();
      }
    }
  }
</script>

(3) 补充知识:测试浏览器对视频封装格式/编码格式的支持情况

// 在浏览器控制台输入以下代码便可查看当前浏览器对常用视频编码格式的支持情况了
var types = ["video/webm",
"video/mp4",
            "audio/webm",
            "video/webm\;codecs=vp8",
            "video/webm\;codecs=daala",
            "video/webm\;codecs=h264",
            "audio/webm\;codecs=opus",
            "video/mpeg"];

for (var i in types) {
console.log( "Is " + types[i] + " supported? " + (MediaRecorder.isTypeSupported(types[i]) ? "Maybe!" : "Nope :("));
}

(4) 注意事项

通过 MediaDevices.getUserMedia() 获取用户多媒体权限时,需要注意其只能正常工作于以下三种环境:

  • localhost

  • 开启了 HTTPS 的域

  • 使用 file:///
    协议打开的本地文件

其他情况下,比如在一个 HTTP
站点上,navigator.mediaDevices
的值为 undefined

如果想要 HTTP
环境下也能使用和调试 MediaDevices.getUserMedia()
,可通过开启 Chrome 的相应参数。


开启相应 flag:

通过传递相应参数来启动 Chrome Insecure origins treated as secure
flag 并填入相应白名单。

  • 打开 chrome://flags/#unsafely-treat-insecure-origin-as-secure

  • 将该 flag 切换成 enable
    状态

  • 输入框中填写需要开启的域名,譬如 http://example.com"
    ,多个以逗号分隔。

  • 重启后生效。


4. 其他方案

历史调研结果:

解决方案一:难度:4/10js调用第三方截屏软件进行录屏,随后生成录屏文件难点:由于使用第三方软件,不可使用浏览器回调,无法得知录屏软件是否开启,及是否正常工作,同时呼出软件受操作系统限制,成功率较低,如果录屏软件没有提前开启过,成功率无限接近为0优点:如果使用绿色软件不会对内存造成太大的影响,同时码率,帧数,保存格式不受浏览器限制(还是受内存及其他硬件影响)

解决方案二:难度:9/10使用 html2canvas 进行快速截图(1s/24次),随后生成流文件 难点:1.内存占用过大,同时系统运行容易崩溃,以及不可预知错误发生2.格式问题,js可选格式有限,视频码率较低,最高也仅支持24fps

优点:典型费力不讨好

解决方案三:难度:7/10使用第三方库进行录屏,随后生成视频文件难点:国内没有可以参考的第三方库,国外有一个类似的库可供参考,但文档内容晦涩难懂没有中文翻译,在可理解范围内了解到该库功能强大,但定制困难,可供选择视频格式受限,码率及帧数受限浏览器,下载后的视频仅支持浏览器播放(也可通过第三方软件转码的方式转为其他格式)。优点:国外拥有大批用户,同时社区发展良好,git更新及时(意味着不太可能会在这个库上出现bug,有也会及时修复,ps:指库),优化极好,内存占用较小,支持多方框架,包含vue


5.参考资料

(1)视频容器格式与编码格式简介

(2)MediaRecorder网上示例缺陷

(3)MediaRecorder支持录制类型情况

(4)MDN

(5)第三方插件地址

(6)MediaDevices.getUserMedia` undefined 的问题解决


文章转载自布尔科技技术团队,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论