微信小程序 WebView 支持 WebRTC

最近微信小程序的业务场景,使用 WebRTC 对设备进行拉流,针对遇到的一些问题简单记录分享下。

一、音视频传输链路

通过上面的图,可以清楚的看到,分为两条链路

  • 设备 SDK 支持 WebRTC,小程序端可以直接跟设备端进行 WebRTC 通信
  • 设备 SDK 不支持 WebRTC,小程序端可以通过 WebRTC 跟流媒体通信,流媒体通过自研 p2p 跟设备通信

相比走流媒体转发,小程序直接跟设备通信有两大优势(特别是当 candidate 组合为 host 或 srflx 时)

  • 首帧出流速度整体会更快
  • 会更加节省云端流量

二、小程序端 WebRTC 兼容性

找到一张腾讯云提供的兼容性表格,仅供参考:

目前,我们测试过的一些机型,持续测试中,后续有遇到不兼容的机型,也会记录在表格

机型 系统版本 微信版本 是否正常
三星Galaxy NOTE9/N9600 8.1.0 8.0.42
OPPO realme Q9 11 8.0.42
小米10青春版本 11 8.0.42
华为nova8/ANG-AN00 11 8.0.42
华为p40 pro 鸿蒙4.0 8.0.41
iPhone 12 15.4.1 8.0.43
iPhone Xr 14.0.1 8.0.43

PC 环境中其实 WebRTC的支持已经非常好,移动端一直有所欠缺;这几年微信对 WebRTC的支持,移动端 WebRTC的生态预计将越来越好

三、 WebRTC offer 控制

我们的业务场景,只是去获取设备端的音视频,以及对讲的时候,会向设备传输音频,但不会向设备传输视频。因此在协商的时候,需要简单对 offer 处理下

  • 第一种方式,通过在 createOffer 的时候,通过 options 进行控制,但offerToReceiveVideo 是控制是否接收视频(需要控制的是是否发送视频),且 options 已被废弃,因此行不通
  • 第二种方式,使用 addTransceiver 进行控制: addTransceiver('video', { direction: 'recvonly' }),代表仅接收不发送

补充一点,addTransceiver、addTrack、addStream 容易混淆,简单小结:

  • addStream 已弃用,可以不考虑
  • addTransceiver、addTrack 均可用,但要对 offer 进行控制时,addTransceiver 更加方便

四、移动端 video 视频自动播放

我们的业务场景是,进入页面,直接对设备进行拉流,且需要自动播放画面跟声音。根据之前在 PC 端的经验,我的第一反应是没有办法实现带声音的自动播放,如果是静音 autoplay 才可行。

事实证明,很多时候经验并非事实,一切得根据测试实际结果来看!

先说结论: 经过代码兼容测试,最终的效果是,在 WebRTC 这种场景下,无论是安卓或ios,均可实现带声音自动播放视频。

代码层做了哪些兼容?

微信官方文档

javascript 复制代码
// 引入微信 sdk, 1.6.0 版本
<script src="https://res2.wx.qq.com/open/js/jweixin-1.6.0.js" />

// 调用 config 初始化
window.wx.config({
  debug: false,
  appId: 'xxx',
  timestamp: '',
  nonceStr: '',
  signature: '',
  jsApiList: []
});

// ready 回调中手动调用 video.plsy()
window.wx.ready(() => {
  video.play();
})

// video 标签设置为 autoplay

代码层其实就做了上述兼容,就实现了 WebRTC 场景下(注意这里强调了是 WebRTC 场景,普通 mp4 并不行)安卓、ios下带声音自动播放。更准确的说,上述代码,其实只是针对 ios 下部分机型,不能带声音自动播放的兼容。

自动播放端倪

先上一个表格,这样会比较清晰,在未做任何兼容的情况下:

WebRTC video 带声音自动播放视频 mp4 video 带声音自动播放视频
安卓 可以 不可以
ios 部分机型可以 不可以
PC端 不可以 不可以

通过表格发现,WebRTC video 在未做兼容的情况下,表现跟 mp4 video 是不一样的,两者到底差在哪儿呢?

经过测试得知,主要差别在 video 标签播放视频流的方式不一样:

javascript 复制代码
// WebRTC video
video.srcObject = mediaStream

// mp4 video
<video src='xxxxxx.mp4' />

使用 'srcObject' 的方式,在安卓跟部分 ios 下,直接就可以带声音自动播放;而使用 'src' 的方式,移动端跟 PC 都是不行的。这部分根据实际测试结果得来,并非官方文档,有小伙伴知道官方规范在哪里的,可以告知一下。

五、ios 自动全屏问题

移动端 ios video 播放时,会自动全屏,添加如下属性即可

html 复制代码
<video playsinline webkit-playsinline />

六、视频旋转、比例拉伸问题

我们的设备,推流过来的视频比例大部分为 16:9, 且带有旋转角度 90/270,因此针对有旋转角度的设备,需要进行旋转及拉伸操作,才能铺满整个屏幕。

实际做下来的效果还是ok的,因为 16:9 的比例跟大部分的机型比例差的不太多

代码比较简单,纯 css transform(rotate、scale)实现即可

七、监听设备拉流状态、计算视频流 kb/s

测试过程中发现一种场景,WebRTC 建连成功,但画面黑屏。排查下来是设备端没有推流过来,针对这种场景,前端希望感知到,并进行重试按钮的展示。

可以基于 WebRTC RTCPeerConnection getStats 方法实时获取收到的数据,以及解码成功的数据。代码示意如下:

javascript 复制代码
const stats = await RTCPeerConnectioIns.getStats(null);
stats.forEach((report) => {
    if (
      report.type === 'inbound-rtp' &&
      (report.mediaType === 'video' || report.kind === 'video')
    ) {
      if (report.bytesReceived === 0) {
        // 未收到数据
        return;
      }
      if (report.keyFramesDecoded === 0 && report.framesDecoded === 0) {
        // 解码失败
        return;
      }

      // 收到数据且解码成功
    }
});

另外,产品希望在页面上实时展示一秒钟收到的视频数据(kb/s)。实现方式也是基于上述 API 来做即可。代码示意:

javascript 复制代码
const stats = await RTCPeerConnectioIns.getStats(null);
let kbReceived = 0;
stats.forEach((report) => {
    if (
      report.type === 'inbound-rtp' &&
      (report.mediaType === 'video' || report.kind === 'video')
    ) {
      kbReceived = Math.trunc(report.bytesReceived / 1024);
    }
});
const curTime = Math.trunc(new Date().getTime() / 1000);

// kb/s
const speed = kbReceived / (curTime - 拉流成功时的时间);

八、WebRTC 对讲变声处理

之前在 PC 端的对讲,没有过变声的需求。趁小程序有变声的场景,简单了解了下如何来做变声。

获取麦克风权限兼容

对讲需要获取麦克风权限,需要简单兼容一下 API

javascript 复制代码
getUserMediaUtil(constrains, success, error, fail) {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      // 最新标准 API
      navigator.mediaDevices
        .getUserMedia(constrains)
        .then(success)
        .catch(error);
    } else if (navigator.webkitGetUserMedia) {
      // webkit 内核浏览器
      navigator.webkitGetUserMedia(constrains, success, error);
    } else if (navigator.mozGetUserMedia) {
      // Firefox 浏览器
      navigator.mozGetUserMedia(constrains, success, error);
    } else if (navigator.getUserMedia) {
      // 旧版 API
      navigator.getUserMedia(constrains, success, error);
    } else {
      fail();
    }
  }
变声方法

变声实际上跟 WebRTC 没有什么关系,只要前置处理好音频数据,然后塞给 WebRTC 即可

第一种方式是基于 js 自行处理,借助 web audio API。奇舞团这篇文章不错,可以参考看看。对于一些简单的变调且变速的,基于 js 实现比较方便

第二种方式是,借用 wasm 调用 c++ 提供的内部音频处理算法

  • 我们的处理流程是 mediastream 转 pcm 塞给 c++ 处理, c++ 处理完回调处理后的 pcm 给前端,前端将 pcm 转 mediastream 喂给 WebRTC 音频通道
  • 一般对于变调不变速(我们的场景),或者是变速不变调,直接采用 js 实现相对复杂,此时可借助成熟的算法,配合 wasm 来处理
  • wasm 处理音频相对简单,不像视频那么耗 CPU

小结

针对需要在微信小程序视频通信的场景,通过 WebView 使用 WebRTC 或许是一个不错的选择。

相比安装庞大的 APP, 小程序显得更加轻量便捷,比如家用安防摄像头,看猫的摄像头等比较轻量的业务场景。

备注: 文章中若有错误,还请指出,这边会及时更正,感谢。

相关推荐
罔闻_spider32 分钟前
爬虫----webpack
前端·爬虫·webpack
吱吱鼠叔33 分钟前
MATLAB数据文件读写:1.格式化读写文件
前端·数据库·matlab
爱喝水的小鼠1 小时前
Vue3(一) Vite创建Vue3工程,选项式API与组合式API;setup的使用;Vue中的响应式ref,reactive
前端·javascript·vue.js
WeiShuai1 小时前
vue-cli3使用DllPlugin优化webpack打包性能
前端·javascript
Wandra1 小时前
很全但是超级易懂的border-radius讲解,让你快速回忆和上手
前端
ice___Cpu1 小时前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端
JYbill1 小时前
nestjs使用ESM模块化
前端
加油吧x青年2 小时前
Web端开启直播技术方案分享
前端·webrtc·直播
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(二)
前端·react.js·前端框架
小白小白从不日白2 小时前
react hooks--useCallback
前端·react.js·前端框架