微信小程序 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, 小程序显得更加轻量便捷,比如家用安防摄像头,看猫的摄像头等比较轻量的业务场景。

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

相关推荐
xiaofeichaichai1 小时前
Webpack
前端·webpack·node.js
Thecozzy1 小时前
线上 Bug 排查与修复实录
架构
鹏大师运维1 小时前
为什么信创电脑装软件总提示“软件包架构不匹配”?
linux·运维·架构·国产化·麒麟·deb·统信uos
问心无愧05131 小时前
ctf show web入门111
android·前端·笔记
唐某人丶2 小时前
模型越来越强,我们还需要 Agent 工程吗?—— 从价值重估到 Harness 实践
前端·agent·ai编程
智码看视界2 小时前
现代Web开发基础:全栈工程师的起航点
前端·后端·c5全栈
JS菌2 小时前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
excel3 小时前
HLS TS 文件损坏的元凶:Git 提交与拉取
前端
Aphasia3113 小时前
https连接传输流程
前端·面试