听说微信视频号前阵子又上(gao)新(shi)啦,悄悄告诉你,手机外的硬件设备也能通过微信视频号直播啦,利用专业的设备提供第一视角的直播体验。
下面我们来跟大家介绍下这个功能背后关于技(jia)术(ban)的故事。为了实现这一功能,我们的产品说很简单啊,你们搞个SDK,然后啪一声给到接入方,他调用你的接口开播,把音视频数据给你,你推到微信后台,然后从微信后台拉回直播间数据展示出来就可以了。听起来好像没什么毛病,接下来我们就来说说这个实现起来有多简单。
技术路线背景
首先为了实现这个功能,我们需要解决的问题主要有以下几点:
- 微信用户身份
- 网络信令通道
- 界面显示和交互
- 音视频编码&推流
而我们需要实现的平台,包括但不限于:移动平台(Android/iOS)、PC平台(Windows/Mac)和嵌入式平台(以Raspberrypi为例)
为了节省人力成本,我们决定走跨平台的技术路线,将要解决的二维问题降成一维。下面开始对要解决的4个问题逐个进行分析。
微信用户身份
由于我们是作为一个SDK集成在接入方的App内的,SDK需要跟微信后台通信,需要让后台知道我们是谁,是哪个微信用户在使用,所以我们就需要一个微信用户的身份。这个问题比较好解决,目前的微信开放平台就支持,主要包括了跳转微信授权登陆和微信扫码登陆,其中跳转微信授权登陆的主要适用于移动App的场景,跳转微信授权的大致流程如下所示:
微信扫码登陆主要适用于PC平台等无法跳转微信授权的场景,扫码登陆的大致流程如下所示:
利用这两种模式,我们确定了以下的设备授权方案,来满足可能接入的所有设备类型:
网络信令通道
解决了微信用户的身份问题以后,我们来看看网络信令的问题。在直播开始前我们需要对直播间进行设置,直播过程中需要显示评论跟观众互动,结束直播时需要通知后台。这所有的功能都涉及到跟微信后台通信的问题,所以我们需要一个安全并且稳定的网络组件,收发网络请求和接收后台的消息推送。这个网络组件必须具备如下图所示的模块和能力:
那么我们是否需要搭建一整套的网络通信组件呢,还好前人栽树后人乘凉,我们有开源的微信mars组件,能为我们提供稳定的网络服务。而对于我们直播业务CGI请求来说,要解决的问题就剩下两个了:
- 制定一套与后台的通信协议进行打包/解包,数据加解密;
- 为用户做身份鉴权
幸运的是这两个问题微信智联平台(WeChat iLink)刚好能帮我们解决,以下简称ilink平台。ilink平台提供了一套基于云平台的智能物联网解决方案,能够赋予设备及用户独立的身份来使用微信的服务。接入ilink平台后的整体结构如下所示:
ilink平台提供了一套跨平台的接入组件,目前我们只需要用到其中的两个模块:ilink-network和ilink-tdi。其中ilink-network就集成了微信mars组件,负责ilink平台的通信协议和安全保障。ilink-tdi则提供了ilink平台的统一登陆接入服务,提供和维护用户的登陆态等信息。因此,我们最终只要实现业务的CGI就可以了。
界面显示及交互
在直播流程中,我们需要对直播间进行设置,查看直播间互动信息等操作,那么就存在界面显示及交互实现的问题,先看看业务的界面和交互长啥样:
明确需求后,我们看看如何简单高效地来解决界面显示及交互的问题。在上一阶段中我们已经在跨平台组件内实现了业务的CGI,也就是说数据已经有了,我们现在需要做的就是利用这些数据实现上面的界面。因为我们最终是实现一个SDK集成到接入方的应用上的,那么在我们的业务场景下,我们来分析下以下各种方案的优劣性:
方案 | 原生 | Flutter | React Native | H5 |
---|---|---|---|---|
性能 | 优秀 | 良好 | 良好 | 中等 |
多平台实现效率 | 低 | 中 | 中 | 高 |
UI更新 | 困难 | 较难 | 较难 | 容易 |
接入成本 | 较低 | 高 | 较高 | 低 |
跨平台 | 不支持 | 支持 | 适配后支持 | 支持 |
原生实现的话能充分发挥出各个平台的性能,但是在多平台的开发效率及版本更新上有很大的软肋,而直播组件作为一个SDK,需要厂商更新以后才能发版本。Flutter和React Native能够减少一些跨平台的开发成本,但是在版本更新上也存在不足,厂商接入成本也比较高。考虑到直播间评论展示和消息互动本身对性能要求不高,同时H5在其他方面都有很好的优势,接入方也只需要提供一个WebView即可,因此我们采用了H5的方案。确定好H5方案后,我们需要做的就是为不同平台生成对应的jsbridge脚本,并在接入方提供的WebView上将该脚本注入,为H5提供对应的jsapi接口来控制直播和收发互动消息即可。加上前面已经集成的授权和信令通道以后,软件架构如下所示:
音视频编码&推流
这里简单介绍下音视频编码&推流,一般我们将摄像头和麦克风采集到的数据称为音视频数据,将音视频数据推送到直播后台(推流)后,观众就能看到画面和听到声音了。但是摄像头和麦克风采集到的数据往往是没有经过编码的,数据量非常大,所以我们一般需要对其进行音视频编码,降低数据量后再进行传输,最后在观众端进行解码就能还原画面和声音了。
为了兼容市面上的设备及降低接入成本,我们决定采用标准的Rtmp协议进行推流直播。虽然市面上已经有很多厂商提供直播SDK了,但是一方面这些SDK基本是与自己平台的直播服务绑定的,另一方面一般都只提供移动平台的版本库,能同时满足我们所有平台需求的就更少了,而且也不会开源代码,没办法自己做移植。因此我们需要自己封装一个跨平台的音视频编码&推流模块,当然从零开始搞是不现实的,我们需要从现有的零件中选择适合我们的来组装自己的轮子。
由于我们是作为一个SDK集成在接入方的App内的,为了降低对接入方的影响,我们需要在SDK内做一个跨平台的轻量级的音视频编码&推流模块,而且SDK不能给接入方带来开源协议上的风险。所以我们放弃了FFmpeg、OpenCV、Gstreamer等集成化的方案。为了实现该模块我们主要解决的是以下几个问题:
- 视频源数据格式转换(统一格式)
- H.264视频编码
- AAC音频编码
- Rtmp推流
针对上面需要解决的问题,我们逐个做了方案对比和筛选,最后确定了当前一期的实现方案,以libyuv openh264 fdk-aac(enc) libsrs-rtmp为基础,在软件设计上,主要采用了流水线和模块化的结构,满足不同源数据格式的输入及后续升级模块的需求:
通过多队列数据缓存,多线程数据处理来确保整个编码和推流过程的流畅性。通过对所有数据产生的时间记录来保证音视频同步。在Android版本的DemoApp中,我们将直播参数进行了如下设置:
视频流:25帧/秒,1280*720预览分辨率,H.264编码,1280*720编码输出分辨率,1.2Mbps
音频流:44.1kHz,双声道,16bit,AAC-LC编码,128kbps
在该配置下,在Google Pixel C上运行时,开播前只做摄像头预览,开播后将获取到的RGBA数据给到组件进行推流后CPU占用和内存占用对比如下:
CPU占用 | 设备内存占用 | |
---|---|---|
开播前 | 8%左右 | 234MB |
开播后 | 32%左右 | 257.8MB |
至此我们已经实现了一个全平台通用的音视频编码&推流模块了,但是美中不足之处在于使用的是软件编码,所以在运行的过程中CPU占用会比较高,随之而来的就是发热和耗电的问题了。而目前大多数平台是有提供硬件编码的能力的,所谓硬件编码就是使用非CPU进行编码,一般是显卡GPU或专用芯片等,优点是性能高、CPU占用低。
因此为了充分利用各平台的优势,我们决定优先在移动平台(Android/iOS)引入硬编码的实现,在没有硬编模块、硬编模块格式不支持或启动失败等情况下使用软件编码作为兜底逻辑。引入硬件编解码模块后,Android平台增加相关平台逻辑后如下所示:
iOS平台增加相关平台逻辑后如下所示:
在引入了硬件编码模块以后,我们使用Google Pixel C和iPhone Xs Max在相同的音视频编码配置下,跟软件编码做了一次对比:
从测试结果看,引入硬件编码确实能为我们带来一些性能上的提升。而在集成了音视频编码&推流能力以后,直播组件的软件架构如下所示:
总结
至此,我们针对需要解决的问题都有了确定的解决方案了:
- 微信用户身份:跳转微信授权或扫码授权
- 网络信令通道:ilink-network和ilink-tdi
- 界面显示和交互:H5 Jsbridge Jsapi
- 音视频编码&推流:libyuv openh264 fdk-aac(enc) libsrs-rtmp 移动平台硬件编码
下面是我们开发的Demo应用在集成整个直播组件前后做的一个对比,目前对外提供的都是动态库的形式:
DemoApp版本 | 集成组件前 | 集成组件后 | 增量大小 |
---|---|---|---|
Android(armeabi-v7a) | 4.9MB | 9.7MB | 4.8MB |
Android(arm64-v8a) | 4.9MB | 11MB | 6.1MB |
iOS(arm64) | 6.6MB | 15.6MB | 9MB |
以上就是本次跟大家分享的微信跨平台硬件直播组件背后的故事,目前视频号硬件直播已经对外开放申请,详情请看:https://wecooper.weixin.qq.com
最后是广告,如需接入视频号硬件直播能力,请联系:WeCooper@tencent.com