- 2019-03-07
- 小程序架构设计(一)
在微信早期,我们内部就有这样的诉求,在微信打开的H5可以调用到微信原生一些能力,例如公众号文章里可以打开公众号的Profile页。所以早期微信提供了Webview到原生的通信机制,在Webview里注入JSBridge的接口,使得H5可以通过它调用到原生能力。 [图片] 我们可以通过JSBridge微信预览图片的功能: [代码]WeixinJSBridge.invoke('imagePreview', { current: https://img1.gtimg.com/1.jpg', urls: [ 'https://img1.gtimg.com/1.jpg', 'https://img1.gtimg.com/2.jpg', 'https://img1.gtimg.com/3.jpg' ] }) [代码] 早期微信官方是没有暴露这些接口的,都是腾讯内部业务在使用,很多外部开发者发现后,就依葫芦画瓢地使用了。 从另外一个角度看,JSBridge是微信和H5的通信协议,有一些能力可能要组合不同的能力才能完整调用。如果我们直接开放这套API,相当于所有开发者都要直接理解这样的接口协议,显然是很不合理的。 所以在2015年初的时候,微信就发布了JSSDK,其实就是隐藏了内部一些细节,包装了几十个API给到上层业务直接调用。 [图片] 前边的代码就变成了: [代码]wx.previewImage({ current: https://img1.gtimg.com/1.jpg', urls: [ 'https://img1.gtimg.com/1.jpg', 'https://img1.gtimg.com/2.jpg', 'https://img1.gtimg.com/3.jpg' ] }) [代码] 开发者可以用JSSDK来调用微信的能力,来完成一些以前H5做不到或者难以做到的事情。 能力上得到了更多的支持,但是微信里的H5体验却没有改善。 第一点是加载H5时的白屏。在微信里打开链接后会看到白屏,有一些H5的服务不稳定,这个白屏现象会更严重。 [图片] 第二点是在H5跳转到其他页面时,切换的效果也很不流畅,只能看到顶部绿色进度条在走。 [图片] 随着JSSDK的开放,还出现了更不好对付的问题。 微信上越来越多干坏事的人,有人做假红包,有人诱导分享,有伪造一些官方活动。他们会利用JSSDK的分享能力变相的去裂变分享到各个群或者朋友圈,由于JSSDK是根据域名来赋予api权限的,运营人员封了一个域名后,他们立马用别的域名又继续做坏,要知道注册一个新的域名的成本是很低的。 [图片] [图片] [图片] 龙哥在2016年微信公开课上提出了应用号的概念,我们要重新设计一个新的移动应用开发模式,同时我们要解决刚刚提到的一些问题。 至此,我们回顾一下目前移动应用开发的一些特点: Web开发的门槛比较低,而App开发门槛偏高而且需要考虑iOS和安卓多个平台; 刚刚说到H5会有白屏和页面切换不流畅的问题,原生App的体验就很好了; H5最大的优点是随时可以上线更新,但是App的更新就比较慢,需要审核上架,还需要用户主动安装更新。 我们更想要的一种开发模式应该是要满足一下几点: 像H5一样开发门槛低; 体验一定要好,要尽可能的接近原生App体验; 让开发者可以云端更新,而且我们平台要可以管控。 很多人可能会第一时间想到Facebook的React Native(下边简称RN),是不是RN就能解决这些问题呢? 是的,React Native貌似可以解决刚刚那些问题,我们也曾经想用RN来做。但是仔细分析了一下,我们发现了采用RN这个机制做开放平台还是存在一些问题。 RN只支持CSS的子集,作为一个开放的生态,我们还要告诉外边千千万万的开发者,哪些CSS属性能用,哪些不能用; RN本身存在一些问题,这些依赖RN的修复,同时这样就变成太过依赖客户端发版本去解决开发者那边的Bug,这样修复周期太长。 RN前阵子还搞出了一个Lisence问题,对我们来说也是存在隐患的。 [图片] 所以我们舍弃了这样的方案,我们改用了Hybrid的方式。简单点说,就是把H5所有代码打包,一次性Load到本地再打开。这样的好处是我们可以用一种近似Web的方式来开发,同时在体验上也可以做到不错的效果,并且也是可以做到云端更新的。 [图片] 现在留给我们的最后一个问题就是,平台的管控问题。 怎么理解呢?我们知道H5的界面结构是用HTML进行描述,浏览器进行一系列的解析最终绘制在界面上。 [图片] 同时浏览器提供了可以操作界面的DOM API,开发者可以用这些API进行一些界面上的变动,从而实现UI交互。 [图片] 既然我们要采用Web+离线包的方式,那我们要解决前边说过的安全问题,我们就要禁用掉很多危险的HTML标签,还要禁用掉一些API,我们要一直维护这样的白名单或者黑名单,实现成本太高了,而且未来浏览器内核一旦更新,对我们来说都是很大的安全隐患。 [图片] 这就是小程序一开始遇到的问题,在下篇文章《小程序架构设计(二)》,我们再详细展开一下小程序是如何解决以上这个问题的。
2019-02-26 - 微信小程序音视频与WebRTC互通的技术思路和实践
概述 本文介绍了微信小程序视音视频和WebRTC的技术特征、差异等,并针对两者的技术差异分享和总结了微信小程序视音视频和WebRTC互通的实现思路以及技术方案。 关于作者 rexchang(常青):腾讯视频云终端技术总监,2008 年毕业加入腾讯,一直从事客户端研发相关工作,先后参与过 PC QQ、手机QQ、QQ物联 等产品项目,目前在腾讯视频云团队负责音视频终端解决方案的优化和落地工作。 分别介绍一下小程序音视频和WebRTC 小程序音视频是什么? 2017年腾讯视频云团队跟微信团队联合,将视频云 SDK 跟微信小程序整合在一起,并通过 <live-pusher> 和 <live-player> 两个标签的形式开放内部的功能。通过这两个标签,开发者可以实现在线直播、低延时监控、双人视频通话以及多人视频会议等功能 [图片] WebRTC又是什么? WebRTC(Web Real-Time Communication),是一个支持网页浏览器进行实时语音对话或视频对话的技术,是谷歌收购 GIPS 公司而获得的一项技术,在 Chrome 浏览器上无需安装插件,通过 javascript 就可以编写实时音视频通话程序。 小程序音视频和WebRTC的区别在哪里? 如果您跟我一样是一个实用主义者,那我就简单从实用主义角度说一下我的结论:小程序搞定了手机,WebRTC拿下了PC。 如果你对技术原理比较感兴趣,那我们就可以从多个技术的角度去列举两者的区别,下面是一张详细对比的表格: [图片] (区别一):内部原理 小程序音视频是将腾讯视频云的 liteavsdk 嵌入到微信内部实现的,然后通过 <live-pusher> 和 <live-player> 两个标签将 SDK 内部的音视频能力开放出来。所以小程序的标签起到了开发者 API 的作用,而内部的 SDK 则是真正用来实现音视频功能。 WebRTC 由谷歌收购 GIPS 得来(这里不得不提一下,我加入腾讯时所在的第一个团队就是 QQ 团队,当时 QQ 的音视频还是购买的 GIPS 公司的产品,不过由于技术支持不能满足需求,后来我们就转为自研路线了)。所以其技术被完整的保留并且加入到了 Google 的 Chrome 浏览器内核当中。而且最近苹果也已经开始在 Safari 浏览器中支持 WebRTC 的相关能力。 (区别二):传输协议 小程序音视频在直播场景使用了 RTMP 推流协议以及 FLV 播放协议,这两种协议都已经有多年的沉淀而且在互联网上的资料也是汗牛充栋。 小程序音视频在视频通话场景则使用了经过 UDP 改造的 RTMP 协议,相比于普通 RTMP 有更强的抗弱网能力和更低的卡顿率。 小程序音视频内核 LiteAVSDK 的抗弱网演示 WebRTC的底层则是使用RTP和RTCP两种数据协议,其中RTP主要用于音视频数据传输,而 RTCP 则用于传输控制。 (区别三):移动端碎片化问题 小程序音视频由于是微信统一实现的,而且微信团队每个版本都尽量要求功能对齐,否则宁可不上,所以在碎片化问题上基本不存在。 相比之下,WebRTC在这里则要尴尬的多,一方面Android系统的碎片化本身让WebRTC的具体表现呈现“百花齐放”的景象,同时,iOS 目前的内嵌WebView(也就是在微信等APP里打开的各种内嵌网页)不支持WebRTC也还是个很麻烦的问题。 (区别四):未来扩展性 小程序音视频跟随微信的版本发布,有什么问题一般是当前代码流修正,然后跟随下一个版本发布,所以一般一个功能点(比如给 pusher 加一个美颜的功能)或者一个问题点(比如不支持手势放大)从确立到最终实现(或解决)仅需要一个月的时间,而且微信APP新版本的覆盖速度也确实挺快。 相比之下,WebRTC则不是一个团队或者一家公司的问题了,因为它现在已经走标准路线,所以每一个新特性都是先确定标准,然后再推动浏览器厂商(包括苹果)进行跟随。 (区别五): 桌面浏览器 在前面几个问题的分析上,我的观点都倾向于小程序音视频。确实,在目前国内的移动领域里,谷歌和苹果都不能一家说了算,真正说了算的还是微信。 但是在桌面浏览器这个部分,Chrome目前在PC浏览器市场上留到地位的存在决定了 WebRTC 的优势就很大了,开发者可以在不安装插件的情况下就可以实现自己想要的功能。 所以,实现同 Chrome 浏览器的音视频互通,成为了小程序音视频的一个必不可少的能力特性。 互联互通 小程序音视频和WebRTC支架并非零和博弈,双方都有自己的优势和不足,实现两者的互通就能实现 1 + 1 > 2 的效果。 PC 端 用户可以使用 Chrome 浏览器直接使用音视频能力,免去安装桌面应用程序的痛苦。 移动端 用户可以使用微信小程序直接使用音视频能力,减少安装App的等待时间。 两者结合,可以将原本局限于小应用场景下的音视频能力扩展到各行各业中。 当然,要实现互联互通,并不是特别容易,首先,我们需要对 WebRTC 协议本身有一个全面的了解: 剖析WebRTC 就像结婚一样,既然你决定要选择另一个人作为人生下半辈子的伴侣,那你肯定会先深入地了解一下TA这个人,比如性格,脾气,爱好等各个方面。 同样,我们要想很好的将小程序音视频和WebRTC打通,那也必须要多了解一下WebRTC,对其知根知底,方能和平相处。 WebRTC 的设计思路是open的 WebRTC 的接口设计一开始就尽可能把内部细节更多的暴露出来,而不是简单封装一套傻瓜式的接口。这种方案的好处是二次开发的灵活度比较高,比如您可以发现 WebRTC 的 API 可以灵活到操作很多连接细节。 但任何事情都有另一面,WebRTC的学习成本并不低,虽然Google做了很多浅显易懂的PPT来教你怎么 Getting Start,但真要完整的学进去,还是需要静下心来,慢慢啃下去。 WebRTC 有多种后台接入方案 说WebRTC喜欢迁就比人,也是一种比喻,WebRTC所支持的后台架构非常多(比如 Mixer, Mesh,Router),而且谷歌认为这些后台实现方案并不需要给出什么限制和标准,因此也就没有提供统一的后台解决方案。 这种开放式的设计思路非常好,但副作用就是实现成本高。在真刀真枪的项目落地时,没有踩坑经验的开发者就很容易被这种技术门槛挡在门外。尤其是想要将 WebRTC 真正应用到企业级解决方案中,面对录制和存档的刚性需求,就需要花费大量时间进行定制开发。 互通方案 了解到 WebRTC 的这些特点后,我们的互通方案也就比较清晰了: 首先,小程序音视频的特点是接口简单,快速上手,这是小程序的优势;而这一点恰恰是WebRTC的劣势,所以我们没有必要在小程序端为WebRTC暴露十几组接口函数,而是继续采用小程序音视频的<live-pusher> 和 <live-player> 标签来解决问题。 其次,WebRTC 的后台没有官方实现,那就意味着这里有很大的发挥空间,腾讯视频云就可以实现一套WebRTC后台并将其同小程序音视频所使用RTMP后台进行打通。简单来说,腾讯视频云要在小程序音视频和WebRTC之间充当红娘(更确切的说,应该是翻译员)的角色。 但是看过《新闻联播》里国家领导人之间谈话镜头的人都知道,这种翻译是会影响交流速度的。小程序音视频和WebRTC之间互通,中间引入一个翻译员,是不是通讯延时也就增加了? 其实不会,因为小程序音视频和WebRTC的视频编码标准在常规应用场景中是一致的,都是H.264标准,只是音频格式不同而已。这就意味着,翻译员要做的事情很少,两边基本都能听懂对方在说什么,所以延时不会增加多少。 协议握手 下图所展示的就是腾讯视频云在小程序音视频和WebRTC互通问题上所采取的方案: [图片] (1)首先,微信端的小程序通过腾讯视频云SDK将音视频流推送到腾讯云 RTMP 服务器。 (2)其次,腾讯云 RTMP 服务器的会对音视频数据进行初步的转化处理,然后透传给腾讯视频云的实时音视频后台集群。 (3)再次,实时音视频后台会再次将数据交给一个叫做 WebRTC-Proxy 的模块,就在这里, WebRTC-Proxy 要将来自小程序音视频的音视频数据翻译成 WebRTC 理解的“语言”。 (4)最后,在PC上的Chrome浏览器,就可以通过浏览器内置的WebRTC模块跟 WebRTC-Proxy 通讯,进而看到小程序端的视频影像。 (5)上面的四个过程倒过来,就可以实现双向视频通话;而将腾讯视频云作为星型结构的中心节点,多个端(不管是小程序还是Chrome浏览器)都接入进来,那就可以形成多人音视频解决方案。 打通房间逻辑 仅仅完成了音视频数据在小程序和WebRTC之间的握手还远远不够,因为在一次成功的音视频通话背后,不仅仅是把一端的音视频数据传递到另一端这么简单,还有状态的同步和成员间的状态协同。 比如多人视频通话中,涉及到呼叫和接通的流程,其中一方如果挂断了,其他人要收到挂断的通知。同时,如果有新的参与者加入,那么其他人也要收到相应的通知。WebRTC 中有很多组件,比如 RTCPeerConnection 就负责处理网络连接中各种密密麻麻的逻辑细节。但是 WebRTC 的接口中引入的新名词非常多,对于初学者来说还是有一定的门槛,为了简化这里的逻辑,我们引入一个叫做“房间”的概念。 所谓房间(Room),就是把同时参与视频通话的各方圈在一起的一个东西。比如双人通话中,通话中的两个人 A 和 B 就可以认为在一个房间中。再比如在多人通话中,通话中的五个人(A B C D E)也可以认为是在一个房间里。 有了房间的概念,那我们就可以对刚才说的状态协同用两个简单的动作描述一下:如果有一个人加入了视频通话,那么就可以理解为他/她已经进房(EnterRoom)了;如果有一个退出了视频通话,那么就可以理解为他/她已经离开房间(LeaveRoom)了。而房间的门板上始终写着:“目前在房间里有哪几个人”。 有了房间的概念,我们就可以将小程序的两个简单的<live-pusher> 和 <live-player>标签,同 WebRTC 那一套复杂的 API 进行功能上的对齐,我们甚至不需要修改我们在第一版中定义的接口,就可以达成这个目标: <live-pusher> 标签:代表房间中的“我”。 <live-player> 标签:代表房间中的“其他人”。 内部逻辑细节 [图片] (1)<live-pusher> 的 url 接口不再传递 rtmp:// 协议的推流地址,而是传递 room:// 协议的推流地址。 (2)<live-pusher> 标签在 start 成功之后,就相当于成功进入一个 room,之后,您可以通过 onPushEvent (PUSH_EVT_ROOM_USERLIST = 1020) 事件,收到房间里还有那些人的信息。在视频通话期间,房间内各个成员的进进出出,也都会通过这个事件通知给您的小程序代码。 (3)ROOM_USERLIST 里每一项都是一个二元组(如果是 1v1 的视频通话,ROOM_USERLIST 里只会有一个人): userid 和 playurl。 userid 代表是哪个用户, playurl 则是这个用户远程画面的播放地址。您要做的只是使用 <live-player> 标签播放这些远程画面的图像和声音而已。 (4)在 WebRTC 这一端,您可以参考我们的 webrtc API,这套 API 相对于 WebRTC 原生的 API,更适合初学者使用。 呃… 您可能会说:“你这也叫简单呀,我感觉还是要写几十行代码,能不能真的做到像一个标签一样简单呢?” 好吧,其实上面四步是我们第一个版本的接入流程,就在我们昨晚这套方案之后,小程序团队刚好推出了自定义组件的机制,于是,我们有了更好的接入方案。 能不能更简单? 如果您希望一天内就打通 webrtc 和 小程序音视频 的互通,那么我推荐您不要从零开始,因为那会耗费您太多时间去踩坑和 bugfix,推荐您直接使用我们封装好的 <webrtc-room> ,这套方案既可以帮助您完成快速接入,又能满足一定的定制需求。 另外,不要忘记在微信=>发现=>小程序=>腾讯云视频云,体验一下腾讯云官方 Demo 中的 WebRTC 互通效果哦。 [图片] <webrtc-room> 功能说明 <webrtc-room> 标签是基于 <live-pusher> 和 <live-player> 实现的用于 WebRTC 互通的自定义组件。用于实现跟 Chrome 和 App SDK 之间的视频通话功能。 版本要求 微信 6.6.6 版本开始支持。 Demo体验 (1) Chrome: 用谷歌浏览器打开 体验页面。 (2) 微信端:发现=>小程序=>搜索“腾讯视频云”,点击 视频通话 页卡,输入相同的房间号。 对接资料 源码地址 源码说明 小程序端源码 Github Chrome端源码 Github 属性定义 属性 类型 默认值 说明 template String ‘float’ 必要,标识组件使用的界面模版。 demo中内置 bigsmall,float,grid三种布局 sdkAppID String 必要,开通实时音视频服务创建应用后分配的 sdkAppID userID String 必要,用户 ID userSig String 必要,身份签名,相当于登录密码的作用 roomID Number 必要,房间号 beauty Number 0 可选, 美颜指数,取值 0 - 9,数值越大效果越明显 whiteness String 0 可选, 美白指数,取值 0 - 9,数值越大效果越明显 muted Boolean false 可选,true 静音 false 不静音 debug Boolean false 可选,true 打印推流 debug 信息 fales 不打印推流 debug 信息 bindRoomEvent Function 必要,监听 <webrtc-room> 组件返回的事件 enableIM Boolean false 可选,是否启用IM bindIMEvent Function 当IM开启时必要,监听 IM 返回的事件 aspect String 9:16 可选, 宽高比3:4, 9:16 minBitrate String 200 可选,最小码率,该数值决定了画面最差的清晰度表现 maxBitrate String 400 可选,最大码率,该数值决定了画面最好的清晰度表现 autoplay Boolean false 可选,进入房间后是否自动显示远程画面 enableCamera Boolean true 可选,开启\关闭摄像头 pureAudioPushMode Number 可选,纯音频推流模式 recordId Number 可选,自动录制时业务自定义id enableCamera Boolean true 是否开启摄像头 smallViewLeft String ‘1vw’ 小窗口距离大画面左边的距离,只在template设置为bigsmall有效 smallViewTop String ‘1vw’ 小窗口距离大画面顶部的距离,只在template设置为bigsmall有效 smallViewWidth String ‘30vw’ 小窗口宽度,只在template设置为bigsmall有效 smallViewHeight String ‘40vw’ 小窗口高度,只在template设置为bigsmall有效 waitingImg String 当微信切到后台时的垫片图片 loadingImg String 画面loading图片 操作接口 <webrtc-room> 组件包含如下操作接口,您需要先通过 selectComponent 获取 <webrtc-room> 标签的引用,之后就可以进行相应的操作了。 函数名 说明 start() 启动 pause() 暂停 resume() 恢复 stop() 停止 switchCamera() 切换摄像头 sendC2CTextMsg(receiveUser, msg, succ, fail) 发送C2C文本消息 sendC2CCustomMsg(receiveUser, msgObj, succ, fail) 发送C2C自定义消息 sendGroupTextMsg(msg, succ, fail) 发送群组文本消息 sendGroupCustomMsg(msgObj, succ, fail) 发送群组自定义消息 [代码]var webrtcroom = this.selectComponent("#webrtcroomid") webrtcroom.pause(); [代码] 事件通知 <webrtc-room> 标签通过 onRoomEvent 返回内部事件,通过 onIMEvent返回 IM 消息事件,事件参数格式如下: [代码]"detail": { "tag": "事件tag标识,具有唯一性", "code": "事件代码", "detail": "对应事件的详细参数" } [代码] 如何快速接入? 上面说了很多细节的技术原理和内部细节,如果您想要快速尝试一下,建议您阅读下面三篇文章就可以了。 一分钟跑通demo 我们准备了一个简单上手的小程序音视频Demo,输入房间号即可开始通话,这篇文章主要介绍如何把Demo快速地 run 起来。 一分钟集成组件 这篇文章主要介绍如何快速地将 <webrtc-room> 组件集成到您的小程序工程项目中。 快速调通基本功能 这篇文档就主要介绍如何二次开发了,它介绍了<webrtc-room> 中主要 API 的使用。 总结 本篇文章主要介绍了小程序音视频和Chrome浏览中重要的WebRTC技术的互通方案,希望能对您的项目开发有所帮助,期待您的反馈。
2019-02-20