产品那边提了个需求,要在小程序里做直播。听上去像是加个播放器的事,真去调研才发现门槛全在代码之外:类目过不过得了、资质有没有、推流地址从哪来、服务端要不要自己搭。技术选型反而是最后才轮到的事。
这篇把当时调研和接入的过程完整记一遍,三条实现路线各自的适用场景,live-pusher 和 live-player 两个组件的参数怎么配,腾讯云直播那边的域名和 CNAME 怎么弄,最后是 IM 互动和直播回放。看完你应该能判断出自己这个项目该走哪条路。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
- 小程序直播的三种实现方案,各自的成本和限制
- 类目和资质门槛,哪些账号根本开不了直播
- 推流和拉流的完整链路,以及两张 API 调用流程图怎么读
live-player的 live 与 RTC 两种模式,min-cache和max-cache怎么调live-pusher的 SD / HD / FHD / RTC 四种模式怎么选- 云直播服务的选型,推流域名与播放域名的 CNAME 配置
- 直播回放和 IM 弹幕互动的接入路径
- 这几年微信和云厂商这边变了什么
# 一、小程序直播功能目前有哪些实现方案
先把路摆出来,一共三条。
内嵌腾讯直播 H5。腾讯直播已改名 NOW 直播,小程序接入工具文档已经找不到了。这条路当时就走不通,列在这里是为了说明它曾经存在过。
小程序原生实现。小程序对直播和 websocket 都做了比较好的封装,通过 live-pusher、live-player 组件和 websocket API 就能实现直播互动功能。这条路自由度最高,弹幕、连麦、礼物这些自定义交互都能做,代价是链路要自己搭,云直播的账单也要自己扛。
接入小程序直播插件实现。小程序直播是微信提供给小程序开发者的直播组件。这条路接入最快,微信把推流端、播放端、商品挂载、回放这一整套都封装好了,代价是交互样式和业务流程基本按微信的来,改不了多少。
我的判断是这样:如果你的诉求就是「电商小程序里加个直播卖货」,走插件;如果你要做的是在线教育、多人连麦、或者直播界面上有大量自定义交互,那只能走原生。这篇后面主要讲原生这条路。
# 二、小程序原生实现直播功能流程
微信小程序从 1.7 开始,为开发者提供了两个新接口,live-pusher 和 live-player,可以在小程序上实现单向的直播功能。通过和其他技术结合,比如 WebRTC,开发者们还可以进一步在小程序直播的基础上实现连麦功能。
# 2.1 先过类目这一关
由于微信对小程序直播功能的类目有限制,限定了特定类目的小程序才能使用。

另外需要注意几点:
- 个人号无法申请使用直播功能
- 社交类目开通直播功能需要相关视频许可和文网文资质许可
- 所以小程序开通直播的业务,要根据产品的目的和场景去申请对应的类目
这条路我建议排在所有技术工作前面。类目和资质走不通,后面代码写得再漂亮也上不了线。
在小程序管理后台,「开发」-「接口设置」中自助开通对应的权限,如下图所示:

# 2.2 整条链路长什么样

微信小程序原生实现直播功能的流程如上图所示。录制端小程序通过 live-pusher 组件对手机摄像头和麦克风的数据进行采集和编码,推流到服务器;服务器端对数据进行加工处理并分发给多个客户端;播放端小程序通过 live-player 组件从云端拉流并进行实时无差异的解码和渲染,从而实现直播小程序完整的互动功能。
这条链路里,小程序只负责最两头的采集和渲染。中间那一大段全是云服务商的活儿。
推流 API 调用流程图:

拉流 API 调用流程图:

这两张图读的时候盯住两件事:状态回调和错误分支。真机上出问题的时候,你能拿到的信息全在 bindstatechange 和 bindnetstatus 这两个事件里,前者报状态码,后者报网络质量。别嫌麻烦,一开始就把这两个回调的日志打全,后面排查能省一半时间。
# 三、小程序直播实现过程
微信小程序中的推拉流功能,需要用到微信提供的 live-player 和 live-pusher 标签。
# 3.1 live-player
live-player 是微信提供的支持实时音视频播放的组件,官方介绍详见组件介绍。
创建 live-player 的演示源码如下:
<live-player
autoplay
wx:if="{{item.playUrl}}"
id="{{item.streamID}}"
mode="RTC"
object-fit="fillCrop"
min-cache="0.1"
max-cache="0.3"
src="{{item.playUrl}}"
debug="{{pushConfig.showLog}}"
bindstatechange="onPlayStateChange"
bindnetstatus="onPlayNetStateChange"
binderror="error">
<cover-view class='character' style='padding: 0 5px;'>{{item.streamID}}</cover-view>
</live-player>
请注意两种模式的区别。
live 模式主要用于直播类场景,比如赛事直播、在线教育、远程培训等等。该模式下,小程序内部的模块会优先保证观看体验的流畅,通过调整 min-cache 和 max-cache 属性,你可以调节观众端所感受到的时间延迟的大小。
RTC 则主要用于双向视频通话或多人视频通话场景,比如金融开会、在线客服、车险定损、培训会议等等。在此模式下,对 min-cache 和 max-cache 的设置不会起作用,因为小程序内部会自动将延迟控制在一个很低的水平,500ms 左右。
那 min-cache 和 max-cache 到底在调什么呢?调的是播放端的缓冲区水位。播放器不会拿到一帧就渲染一帧,它会先囤一小段数据再开始播,用来抵消网络抖动。囤得多,卡顿少但延迟大;囤得少,延迟低但网络一抖就卡。
所以这两个参数不是越小越好,它是延迟和流畅度之间的一个滑块。单向观看的直播,比如带货或者赛事,观众感知不到延迟,可以把水位调高换流畅;需要主播和观众实时对话的场景才值得压低。
还有一个 cover-view。live-player 是原生组件,层级高于普通的 WXML 元素,想在视频上盖字或者盖按钮只能用 cover-view 和 cover-image,普通的 view 会被压在下面看不见。这个坑我踩过,写了半天弹幕层发现真机上什么都看不到。
# 3.2 live-pusher
live-pusher 是微信提供的支持实时音视频录制的组件,官方介绍详见组件介绍。
创建 live-pusher 的演示源码如下:
<live-pusher
wx:if="{{pushUrl}}"
id="video-livePusher"
mode="RTC"
url="{{pushUrl}}"
min-bitrate="{{pushConfig.minBitrate}}"
max-bitrate="{{pushConfig.maxBitrate}}"
aspect="{{pushConfig.aspect}}"
beauty="{{pushConfig.isBeauty}}"
muted="{{pushConfig.isMute}}"
background-mute="true"
debug="{{pushConfig.showLog}}"
bindstatechange="onPushStateChange"
bindnetstatus="onPushNetStateChange">
<cover-view class='character' style='padding: 0 5px;'>{{isPublishing ? "我(" + publishStreamID + ")": ""}}</cover-view>
</live-pusher>
请注意,推流端的模式比播放端多几个。
SD、HD 和 FHD 主要用于直播类场景,比如赛事直播、在线教育、远程培训等等,分别对应三种默认的清晰度。该模式下,小程序会更加注重清晰度和观看的流畅性,不会过分强调低延迟,也不会为了延迟牺牲画质和流畅性。
RTC 则主要用于双向视频通话或多人视频通话场景。该模式下,小程序会更加注重降低点到点的时延,也会优先保证声音的质量,在必要的时候会对画面清晰度和画面的流畅性进行一定的缩水。
min-bitrate 和 max-bitrate 是码率上下限,单位是 kbps。移动端网络波动大,推流端会在这个区间内动态调整。区间给窄了,网络一差就只能丢帧;给宽了,画质会在好网和差网之间明显跳变。这个值没有标准答案,得看你的内容类型,画面静态多的场景可以把上限压低。
background-mute 这个属性容易被忽略,它决定 App 切到后台时是否静音推流。主播接了个电话回来发现直播断了,多半就是后台策略没配对。
# 3.3 服务端的选择
自己搭 RTMP 服务,比如 Nginx rtmp,成本较高,技术实现难度大。所以更现实的做法是用云服务商提供的视频直播服务产品,让它生成推流地址和播放地址。目前市面上主流的云直播产品有腾讯云、阿里云、七牛云等。

各平台均提供内容接入与分发和分布式实时视频处理技术,每个平台提供的功能大同小异但各有千秋。按当时的价格,平均费用大概 20 到 30 元每 100G,100G 流量可以满足 100 人同时在线直播 4 小时。这个数字是 2020 年的行情,现在的定价和赠送政策早就变了,只能当个数量级参考。
接下来选择腾讯云直播进行接入体验。
第一步,申请腾讯云账号,开通云直播权限。当时它会赠送 20GB 流量,超出需要自己花钱。开通流程请参考文档:https://cloud.tencent.com/document/product/454/12517
第二步,域名管理。在这里面会看到两个域名,一个是推流域名,一个是播放域名。域名可以用自己的,建议配置自己的域名,2019 年 2 月 26 日上线查看时发现赠送的播放域名已失效。具体看文档:https://cloud.tencent.com/document/product/267/20381

由于腾讯云不再赠送播放域名,所以需要租用或者使用自己的域名生成播放地址。自己的播放域名不能直接访问,需要完成 CNAME 配置。

CNAME 这一步是很多人卡住的地方。你在腾讯云控制台加了域名,它会给你一个 CNAME 目标值,你得去自己的 DNS 服务商那里把这条解析加上,流量才会真正走到腾讯云的节点。解析生效有延迟,配完先用 dig 或者 nslookup 确认一下再回来调代码,不然你会以为是自己的推流参数写错了。
还有一点,推流地址通常带有过期时间和鉴权签名。这套签名的生成一定要放在自己的后端,不要把密钥打进小程序包里。小程序的代码包是能被解出来的。
# 3.4 组件 API 接入
第一步,live-pusher 推流,也就是数据包实时上传。
使用 live-pusher 发布流,这里用到的参数是 min-bitrate="200" 最小码率,max-bitrate="400" 最大码率,mode="RTC" RTC 模式。加入房间之后我们需要调用 publish 返回一个 rtmp 推流地址。
<live-pusher
autopush
min-bitrate="200"
max-bitrate="400"
mode="RTC"
url="{{publishPath}}">
</live-pusher>
先使用 wx.createLivePusherContext 创建 LivePusherContext,再使用 setData 设置好 publishPath 之后发布: