仓库视频录制方案

1,173 阅读8分钟

背景:

仓库视频录制业务,主要用于客诉追查包裹包装时商品的情况。

仓储订单处理从订单下发、创建波次、拣货、出库包装、发运到交接承运商装车。出库包装作为仓储订单处理是最关键的一环,出库包装时将C端客户的订单内拣货出来的商品进行复核、选择合适的包装箱、根据商品属性进行附加包装材料包装保障运输安全,放入客户的购物清单、发票等。出库包装环节的操作是C端客户进行咨询、查询订单的少货、多货、错货及其他异常的操作依据。因此,定位仓库出库包装环节的操作问题是我们客户及仓库关注的关键点。

出库包装视频单元格功能实现了出库包装从订单扫描到装箱完成的全程监控,便于客户及仓库对订单操作的准确性、包装材料选择及附加填充物规范监控、实现基于云平台的视频追溯。

仓库视频录制方案调研

1、直接安装监控摄像头

这是比较传统的一种包装视频录制的方案,一般是通过找第三方安装一个视频监控,然后24小时录制对应包装台的视频。并且保存到本地的视频服务器。发生客诉时,客服需要视频的时候,仓库会根据订单号的时间,找到对应包装台,然后通过订单包装时间来对之前录制的整个视频进行切割,获取对应时间段的包装视频来供客服查看。

优点

  • 视频清晰
  • 基本不会丢视频
  • 稳定

缺点

  • 成本高
  • 视频录制会一直录制,会存在大量无效视频,浪费磁盘空间
  • 定位到指定时间视频比较麻烦,录制出来的都是整段视频,所以定位视频需要先找到订单包装时间,然后再去切视频,找到对应时间段的视频。
  • 录制存储视频服务器的时间如果和包装台地址不一致,视频的定位会出现错乱

2、使用电脑上外接的摄像头,依赖第三方视频录制工具

出库包装时按照订单进行视频录制,开始扫描订单即开始一个订单视频录制,装箱完成保存视频,并对视频进行订单号-运单号-出库复核时间的水印,按照订单保存视频。这些视频会都保存在包装台上。未来可以将视频上传至客户云平台,方便客户根据订单号、运单号、出库时间进行及时查询。从而实现订单在仓库的关键环节可视化,提高客户满意度。

优点

  • 成本低
  • 视频丢失比较低

缺点

  • 视频保存在包装台,需要额外上传视频到云平台。对本地包装台的存储空间要求比较大。不然上传视频的频率很很高

3、我们的wms系统的视频录制方案

目前的泰坦系统视频录制方案,直接走浏览器录制,视频随订单产生,以面单号命名,录制完后,直接上传视频服务器,包装时才会录制。视频上传到本地视频服务器之后,当产生客诉的时候,视频服务器会触发上传云盘并将链接直接返回给客服。

优点

  • 成本低
  • 定位视频快捷
  • 按订单录制视频,节约磁盘空间

缺点

  • 视频上传丢失可能性比较大,因为浏览器的api接口本身存在bug,另外就是上传视频受网络和视频服务器的硬件限制。当包装台同时上传的视频多的时候,会存在视频上传服务器失败,存本地的情况。目前的处理方案是通过在订单出仓时,用视频标示来防止录制失败的订单出仓。

大概流程如下:

  • wms前端通过浏览器调用摄像头,录制视频后存放在缓存里。
  • 当包装完成后,会将视频流从缓存里取出并发送到本地的一个代理,代理会把视频保存到本地指定文件夹。
  • 本地代理会将上传视频的请求转发到视频服务器。
  • 视频服务器收到视频之后,会返回一个成功的响应。
  • 代理收到响应后会把存在本地的视频删除,然后将成功的响应回传到wms,wms将调用视频的打标接口(表示视频录制并上传到了视频服务器上)。

我们的wms视频录制原理

Q1: 视频录制是如何实现的?

A:

1、首先,需要向用户索要录音的权限,因为浏览器的权限很受限, 代码里调用了 navigator.MediaDevices.getUserMedia() ,获取视频和音频权限。

try {
  // 其中 constraints 为需要获取的权限列表,audio表示音频,video表示视频。
  const result = await navigator.mediaDevices.getUserMedia(constraints);
  
  // 其中成功后得到的结果 为 MediaStream 对象。
  this.stream = result;
    
  // 获取视频标签 <video ref="video" style="height: 155px;" autoplay controls /> 对象
  let video = this.$refs.video;
    
  // 将 MediaStream 与视频标签绑定。
  // 旧的浏览器可能没有srcObject  
  if ('srcObject' in video) {
    video.srcObject = this.stream;
  } else {
    // 防止在新的浏览器里使用它,应为它已经不再支持了
    video.src = window.URL.createObjectURL(this.stream);
  }

  video.onloadedmetadata = function() {
    try {
      video.play();
    } catch (error) {
      console.error(error);
    }
  };
} catch (error) {
  console.error(error);
  this.$message({ message: '无法获取摄像头权限', type: 'warning'});
}

MediaStream 接口是一个媒体内容的流.。一个流包含几个

轨道
,比如视频和音频轨道。

2、创建视频实例,将上一步获取到的 MediaStream 传入 MediaRecorder 的构造器创建一个视频的实例。构造函数的入参可以参照MediaRecorder()。

try {
  let options = {
    audioBitsPerSecond: (this.audioBitRate || 128) * 1000,
    videoBitsPerSecond: (this.videoBitRate || 600) * 1000,
    mimeType: 'video/webm'
  };
  this.recordingLong = false;
  this.recordingChunks = [];
  // 创建视频实例
  this.mediaRecorder = new MediaRecorder(this.stream, options);
  this.mediaRecorder.ondataavailable = this.__handleDataAvailable;
  this.mediaRecorder.start(10);
  this.isRecording = true;,,,
} catch (e) {
  console.log(e);
  return;
}
// 将录制的视频流不断的塞到一个存放视频流的数据中。视频
async __handleDataAvailable(e) {
  this.recordingChunks.push(e.data);
}

3、上面代码中调用了 this.mediaRecorder.start(10) ,start的入参是时间片timeslice, start后开始将视频记录到一个或多个Blob对象中,如果没有传参timeslice,则整体录制的视频流将被记录到一个Blob, 如果像上面代码一般,传了一个10,则表示将每10毫秒录制的视频流存为一个Blob。每次记录了该时间数量的视频流时,会触发ondataavailable 事件。ondataavailable 每次触发,其入参都是之前的视频流,如果在start里没有设置timeslice ,则会在整个视频录制结束的时候才触发ondataavailable 并把视频数据整个的传入,即上面代码中的e.data。 如果设置了timeslice 如上述代码的start(10),则10表示每10毫秒录制的视频存入的blob会传入ondataavailable ,就会每10毫秒触发一次ondataavailable 。上面的代码将陆续录制的数据写入了 this.recordingChunks 。说的不是可能很清楚,可以看下图。

未命名文件 (2).png

4、最后录制的视频流都会存放到一个数组中。。我们工程里的后续处理逻辑如下代码。前端视频录制的流程到这为止。

stopRecording() {
  try {
    //......
    this.mediaRecorder.stop();
    this.isRecording = false;
    const blob = new Blob(this.recordingChunks, { type: 'video/webm' });
    // 最后将这个blob发送到后端然后清空 this.recordingChunks 。到此前端的视频录制完成。
    //...... 
  } catch(e) {
    return;
  }
},

Q2: wms系统的域名是https协议的域名,而视频服务器其局域网的一个服务器,则视频需要从htpps发送的局域网服务器存在 Mixed Content 的问题,可以参考这个链接,还存在跨域限制。前端录制的视频如何转发到仓库局域网视频服务器?

A:

1、前端将录制的视频先转发到了本地的 8443端口,然后通过本地起的代理,从http://localhost 发送 http请求到视频服务器,这么做的目的是绕过在https发送http请求导致的 Mixed Content的安全问题,https 安全协议允许 http后会导致安全协议变的不安全,所以操作是被不允许的。 所以通过这种方式绕过这个行为。

2、现代浏览器认为 http://127.0.0.1:8000/ 是一个“可能可信赖的”链接,因为它指向一个环回地址 ---(正常的数据包会从IP层进入链路层,然后发送到网络上;而给回环地址发送数据包,数据包会直接被发送主机的IP层获取,后面就没有链路层他们啥事了)。发送到 127.0.0.1 的流量肯定不会离开您的计算机,因此它自然而然地不受网络拦截的影响。这意味着如果 Web 应用程序使用HTTPS,并且在 127.0.0.1 上提供原生应用程序的 Web 服务,则两者可以通过 XHR 进行通信。不幸的是, localhost 没有受到相同的待遇。但是系统默认将localhost指向127.0.0.1 ,所以localhost也是一个可以被信赖的链接,这样 wms 系统从 https 向 localhost 发送视频是不被限制的。

3、视频发送到本地的代理服务,然后代理服务会先把视频存储本地,然后同时发送一份视频到 http:// + (视频服务器ip) ,这里做了跨域处理,让局域网两个不同ip之间可以相互通信。 当视频服务器接收到视频后响应成功,代理服务器则会把本地的视频删除,并将服务器的成功响应转发到 wms系统,wms系统则会给发送一个打标请求,告知系统该订单的视频录制上传成功。至此,视频发送的过程完毕。

未命名文件.png

参考链接:

1、https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder/MediaRecorder

2、https://letsencrypt.org/zh-cn/docs/certificates-for-localhost/

3、https://www.jianshu.com/p/ad7cd1d5be45

4、https://w3c.github.io/webappsec-mixed-content/

5、https://developers.google.com/web/tools/chrome-devtools/security

6、https://developer.chrome.com/extensions/xhr