小程序原生实时音视频实现

api文档:https://developers.weixin.qq.com/miniprogram/dev/api/media/live/wx.createLivePusherContext.html

开通标签使用权限:live-pusher 和 live-player 是小程序内部用于支持音视频上行能力的功能标签

  • 目前支持这两个标签的类目如下表格所示(只有非个人主体才有以下类目):
image.png
  • 符合类目要求的小程序,需要在小程序管理后台的【开发】>【接口设置】中自助开通推拉流标签的使用权限:
image.png

live-player:是小程序内部用于支持音视频下行(播放)能力的功能标签。 !!!(基础库 1.7.0 开始支持,低版本需做兼容处理.

属性定义
属性 类型 默认值 必填 说明 最低版本
src string 否 音视频地址。目前仅支持 flv, rtmp 格式 1.7.0
mode string live 否 模式 1.7.0
autoplay boolean false 否 自动播放 1.7.0
muted boolean false 否 是否静音 1.7.0
orientation string vertical 否 画面方向 1.7.0
object-fit string contain 否 填充模式,可选值有 contain,fillCrop 1.7.0
background-mute boolean false 否 进入后台时是否静音(已废弃,默认退后台静音) 1.7.0
min-cache number 1 否 最小缓冲区,单位s(RTC 模式推荐 0.2s) 1.7.0
max-cache number 3 否 最大缓冲区,单位s(RTC 模式推荐 0.8s)。缓冲区用来抵抗网络波动,缓冲数据越多,网络抗性越好,但时延越大。 1.7.0
sound-mode string speaker 否 声音输出方式 1.9.90
auto-pause-if-navigate boolean true 否 当跳转到本小程序的其他页面时,是否自动暂停本页面的实时音视频播放 2.5.0
auto-pause-if-open-native boolean true 否 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放 2.5.0
picture-in-picture-mode string/Array 否 设置小窗模式: push, pop,空字符串或通过数组形式设置多种模式(如: ["push", "pop"]) 2.10.3
referrer-policy string no-referrer 否 格式固定为 https://servicewechat.com/{appid}/{version}/page-frame.html,其中 {appid} 为小程序的 appid,{version} 为小程序的版本号,版本号为 0 表示为开发版、体验版以及审核版本,版本号为 devtools 表示为开发者工具,其余为正式版本; 2.13.0
bindstatechange eventhandle 否 播放状态变化事件,detail = {code} 1.7.0
bindfullscreenchange eventhandle 否 全屏变化事件,detail = {direction, fullScreen} 1.7.0
bindnetstatus eventhandle 否 网络状态通知,detail = {info} 1.9.0
bindaudiovolumenotify eventhandler 否 播放音量大小通知,detail = {} 2.10.0
bindenterpictureinpicture eventhandler 否 播放器进入小窗 2.11.0
bindleavepictureinpicture eventhandler 否 播放器退出小窗 2.11.0
状态码
代码 说明
2001 已经连接服务器
2002 已经连接 RTMP 服务器,开始拉流
2003 网络接收到首个视频数据包(IDR)
2004 视频播放开始
2005 视频播放进度
2006 视频播放结束
2007 视频播放Loading
2008 解码器启动
2009 视频分辨率改变
-2301 网络断连,且经多次重连抢救无效,更多重试请自行重启播放
-2302 获取加速拉流地址失败
2101 当前视频帧解码失败
2102 当前音频帧解码失败
2103 网络断连, 已启动自动重连
2104 网络来包不稳:可能是下行带宽不足,或由于主播端出流不均匀
2105 当前视频播放出现卡顿
2106 硬解启动失败,采用软解
2107 当前视频帧不连续,可能丢帧
2108 当前流硬解第一个I帧失败,SDK自动切软解
3001 RTMP -DNS解析失败
3002 RTMP服务器连接失败
3003 RTMP服务器握手失败
3005 RTMP 读/写失败,之后会发起网络重试
示例代码
  <!-- 
    class 动态选择谁的屏幕放大
    src 播放地址
    mode 模式 live-直播 RTC-实时通话
    binderrorPusher 渲染错误事件,detail = {errMsg, errCode} 
    beauty-style 设置美颜类型 smooth  光滑美颜 nature 自然美颜
    auto-pause-if-open-native 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
    bindstatechange 状态变化事件,detail = {code}
    object-fit 填充模式
    picture-in-picture-mode 设置小窗模式: push, pop,空字符串或通过数组形式设置多种模式(如: ["push", "pop"])
  -->
  <live-player
    id="player"
    src="{{url_user}}" 
    mode="RTC" 
    autoplay 
    bindstatechange="statechangePlayer" 
    beauty-style="smooth" 
    auto-pause-if-open-native="{{false}}"
    object-fit="fillCrop"
    picture-in-picture-mode="['push', 'pop']"
    class="{{is_small == 2 ?'small-window' : 'player'}}" 
    data-class="{{is_small == 2 ?'small-window' : 'pusher'}}"
    bindtap="bindPlayerClick"/>
超低时延

的 RTC 模式支持500ms以内的超低时延链路,可以应用在视频通话和远程遥控等场景中,要使用超低时延播放,需要注意如下几点:
(1)推流端如果是微信小程序,请使用 的 RTC 模式。
(2)推流端如果是 iOS 或者 Android SDK,请使用 setVideoQuality 的 MAIN_PUBLISHER 模式。
(3)推流端如果是 Windows,请不要使用 OBS,延时太高,可以使用我们的 Windows SDK。
(4)min-cache 和 max-cache 请不要自行设置,使用默认值。
(5)播放地址请使用超低延时播放地址,也就是带了防盗链签名的rtmp://地址,如下:
普通直播URL rtmp://3891.liveplay.myqcloud.com/live/3891_test_clock_for_rtmpacc >2s
超低延时URL rtmp://3891.liveplay.myqcloud.com/live/3891_test_clock_for_rtmpacc?bizid=bizid&txTime=5FD4431C&txSerect=20e6d865f462dff61ada209d53c71cf9 < 500ms

对象方法
LivePlayerContext

LivePlayerContext 实例,可通过 wx.createLivePlayerContext 获取。
LivePlayerContext 通过 id 跟一个 live-player 组件绑定,操作对应的 live-player 组件。
wx.createLivePlayerContext('player', this)

LivePlayerContext.play()//播放

LivePlayerContext.stop()//停止

LivePlayerContext.mute()//静音

LivePlayerContext.pause()//暂停

LivePlayerContext.resume()//恢复

LivePlayerContext.requestFullScreen(Object object)//进入全屏

LivePlayerContext.exitFullScreen()//退出全屏

LivePlayerContext.exitPictureInPicture()//退出小窗,该方法可在任意页面调用

LivePlayerContext.snapshot(Object object)//截图

LivePlayerContext.requestPictureInPicture()//进入小窗

var player = wx.createLivePlayerContext('pusher');
player.requestFullScreen({
  success(){
    console.log('enter full screen mode success!')
  }
  fail(){
    console.log('enter full screen mode failed!')
  }
  complete(){
    console.log('enter full screen mode complete!')
  }
});
小窗特性说明

live-player 小窗支持以下三种触发模式(在组件上设置 picture-in-picture-mode 属性):
1.push 模式,即从当前页跳转至下一页时出现小窗(页面栈push)
2.pop 模式,即离开当前页面时触发(页面栈pop)
3.以上两种路由行为均触发小窗

此外,小窗还支持以下特性:
  • 小窗容器尺寸会根据原组件尺寸自动判断
  • 点击小窗,用户会被导航回小窗对应的播放器页面
  • 小窗出现后,用户可点击小窗右上角的关闭按钮或调用 context.exitPictureInPicture() 接口关闭小窗
  • 当播放器进入小窗模式后,播放器所在页面处于 hide 状态(触发 onHide 生命周期),该页面不会被销毁。当小窗被关闭时,播放器所在页面会被 unload (触发 onUnload 生命周期)。
分割线

live-pusher 实时音视频录制(v2.9.1 起支持同层渲染)。需要用户授权 scope.camera、scope.record。

live-pusher支持的类目同live-player一样

属性定义
属性 类型 默认值 必填 说明 最低版本
url string 否 推流地址。目前仅支持 rtmp 格式 1.7.0
mode string RTC 否 SD(标清), HD(高清), FHD(超清), RTC(实时通话) 1.7.0
autopush boolean false 否 自动推流 1.7.0
muted boolean false 否 是否静音。即将废弃,可用 enable-mic 替代 1.7.0
enable-camera boolean true 否 开启摄像头 1.7.0
auto-focus boolean true 否 自动聚集 1.7.0
orientation string vertical 否 画面方向 1.7.0
beauty number 0 否 美颜,取值范围 0-9 ,0 表示关闭 1.7.0
whiteness number 0 否 美白,取值范围 0-9 ,0 表示关闭 1.7.0
aspect string 9:16 否 宽高比,可选值有 3:4, 9:16 1.7.0
min-bitrate number 200 否 最小码率 1.7.0
max-bitrate number 1000 否 最大码率 1.7.0
audio-quality string high 否 高音质(48KHz)或低音质(16KHz),值为high, low 1.7.0
waiting-image string 否 进入后台时推流的等待画面 1.7.0
waiting-image-hash string 否 等待画面资源的MD5值 1.7.0
zoom boolean false 否 调整焦距 2.1.0
device-position string front 否 前置或后置,值为front, back 2.3.0
background-mute boolean false 否 进入后台时是否静音(已废弃,默认退后台静音) 1.7.0
mirror boolean false 否 设置推流画面是否镜像,产生的效果在 live-player 反应到 2.7.0
remote-mirror boolean false 否 同 mirror 属性,后续 mirror 将废弃 2.10.0
local-mirror string auto 否 控制本地预览画面是否镜像 2.10.0
audio-reverb-type number 0 否 音频混响类型 2.10.0
enable-mic boolean true 否 开启或关闭麦克风 2.10.0
enable-agc boolean false 否 是否开启音频自动增益 2.10.0
enable-ans boolean false 否 是否开启音频噪声抑制 2.10.0
audio-volume-type string auto 否 音量类型 2.10.0
video-width number 360 否 上推的视频流的分辨率宽度 2.10.0
video-height number 640 否 上推的视频流的分辨率高度 2.10.0
beauty-style string smooth 否 设置美颜类型 2.12.0
filter string standard 否 设置色彩滤镜 2.12.0
bindstatechange eventhandle 否 状态变化事件,detail = {code} 1.7.0
bindnetstatus eventhandle 否 网络状态通知,detail = {info} 1.9.0
binderror eventhandle 否 渲染错误事件,detail = {errMsg, errCode} 1.7.4
bindbgmstart eventhandle 否 背景音开始播放时触发 2.4.0
bindbgmprogress eventhandle 否 背景音进度变化时触发,detail = {progress, duration} 2.4.0
bindbgmcomplete eventhandle 否 背景音播放完成时触发 2.4.0
bindaudiovolumenotify eventhandle 否 返回麦克风采集的音量大小 2.12.0
状态码
代码 说明
1001 已经连接推流服务器
1002 已经与服务器握手完毕,开始推流
1003 打开摄像头成功
1004 录屏启动成功
1005 推流动态调整分辨率
1006 推流动态调整码率
1007 首帧画面采集完成
1008 编码器启动
-1301 打开摄像头失败
-1302 打开麦克风失败
-1303 视频编码失败
-1304 音频编码失败
-1305 不支持的视频分辨率
-1306 不支持的音频采样率
-1307 网络断连,且经多次重连抢救无效,更多重试请自行重启推流
-1308 开始录屏失败,可能是被用户拒绝
-1309 录屏失败,不支持的Android系统版本,需要5.0以上的系统
-1310 录屏被其他应用打断了
-1311 Android Mic打开成功,但是录不到音频数据
-1312 录屏动态切横竖屏失败
1101 网络状况不佳:上行带宽太小,上传数据受阻
1102 网络断连, 已启动自动重连
1103 硬编码启动失败,采用软编码
1104 视频编码失败
1105 新美颜软编码启动失败,采用老的软编码
1106 新美颜软编码启动失败,采用老的软编码
3001 RTMP -DNS解析失败
3002 RTMP服务器连接失败
3003 RTMP服务器握手失败
3004 RTMP服务器主动断开,请检查推流地址的合法性或防盗链有效期
3005 RTMP 读/写失败
示例代码
  <live-pusher 
    id="pusher"
    url="{{url_doctor}}" 
    mode="RTC" 
    autopush 
    bindstatechange="statechangePusher" 
    beauty-style="smooth"
    binderror="binderrorPusher"
    class="{{is_small == 1 ?'small-window' : 'pusher'}}"
    data-class="{{is_small == 1 ?'small-window' : 'pusher'}}"
    bindtap="bindPusherClick" />
对象方法
LivePusherContext

LivePusherContext 实例,可通过 wx.createLivePusherContext 获取。

LivePusherContext.start() //开始推流,同时开启摄像头预览

LivePusherContext.stop() //停止推流,同时停止摄像头预览

LivePusherContext.pause() //暂停推流

LivePusherContext.resume() //恢复推流

LivePusherContext.switchCamera() //切换前后摄像头

LivePusherContext.snapshot(Object object) //快照

LivePusherContext.toggleTorch() //切换手电筒

LivePusherContext.playBGM(Object object) //播放背景音

LivePusherContext.stopBGM() //停止背景音

LivePusherContext.pauseBGM() //暂停背景音

LivePusherContext.resumeBGM() //恢复背景音

LivePusherContext.setBGMVolume(Object object) //设置背景音音量

LivePusherContext.setMICVolume(Object object) //设置麦克风音量

LivePusherContext.startPreview() //开启摄像头预览

LivePusherContext.stopPreview() //关闭摄像头预览

LivePusherContext.sendMessage(Object object) //发送SEI消息
Bug & Tip
  1. tip:开发者工具上暂不支持。
  2. tip:live-pusher 默认宽度为100%、无默认高度,请通过wxss设置宽高。
  3. tip:waiting-image 属性在 2.3.0 起完整支持网络路径、临时文件和包内路径。
  4. tip:请注意原生组件使用限制。
  5. tip: 相关介绍和原理可参考此文章
附上完整示例

!!!要注意两端推流播放 比如是a端和b端

a端 b端
pusher _a pusher _b
player_b player_a
<!-- wxml-->
<view class="chat">
  <!-- 
    binderrorPusher 渲染错误事件,detail = {errMsg, errCode} 
    bindstatechange 状态变化事件,detail = {code}
    beauty-style 设置美颜类型 smooth  光滑美颜 nature 自然美颜
    beauty  0    美颜,取值范围 0-9 ,0 表示关闭
    device-position front   前置或后置,值为front, back
  -->
  <live-pusher 
    id="pusher"
    class="{{is_small == 1 ?'small-window' : 'pusher'}}" 
    url="{{push_stream}}" 
    mode="RTC" 
    autopush 
    bindstatechange="statechangePusher" 
    beauty-style="smooth"
    binderror="binderrorPusher"
    data-class="{{is_small == 1 ?'small-window' : 'pusher'}}"
    bindtap="bindPusherClick"
    device-position="{{device_position}}" />
  <!-- 
    class 动态选择谁的屏幕放大
    src 播放地址
    mode 模式 live-直播 RTC-实时通话
    binderrorPusher 渲染错误事件,detail = {errMsg, errCode} 
    beauty-style 设置美颜类型 smooth  光滑美颜 nature 自然美颜
    auto-pause-if-open-native 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
    bindstatechange 状态变化事件,detail = {code}
    object-fit 填充模式
    picture-in-picture-mode 设置小窗模式: push, pop,空字符串或通过数组形式设置多种模式(如: ["push", "pop"])
  -->
  <live-player
    id="player"
    src="{{pull_stream}}" 
    mode="RTC" 
    autoplay 
    bindstatechange="statechangePlayer" 
    beauty-style="smooth" 
    auto-pause-if-open-native="{{false}}"
    object-fit="fillCrop"
    picture-in-picture-mode="['push', 'pop']"
    class="{{is_small == 2 ?'small-window' : 'player'}}" 
    data-class="{{is_small == 2 ?'small-window' : 'pusher'}}"
    bindtap="bindPlayerClick"/>
  <view class="fixed">
    <view class="" style="height: auto;">
      <image src="/images/gd.png" bindtap="hangUp"></image>
      <image src="/images/qh.png" bindtap="cutCamera"></image>
    </view>
  </view>
</view>
//js
const app = getApp()
Page({
  data: {
    player: '',
    pusher: '',
    is_small: 2, // 1-pusher 2-player
    pull_stream: 'https://domain/pull_stream',
    push_stream: 'https://domain/push_stream'
  },
  onLoad() {
    this.setData({
      player: wx.createLivePlayerContext('player', this),
      pusher: wx.createLivePusherContext(),
    })
  },
  onReady() {},
  onShow() {},
  //状态变化事件
  statechangePusher(e) {
    const code = e.detail.code
    this.colse(code, 'Pusher')
  },
  //状态变化事件
  statechangePlayer(e) {
    const code = e.detail.code
    // 2004 视频播放开始
    if (code == '2004') {
      // this.data.pusher.pauseBGM()
    }
    this.colse(code, 'Player')
  },
  //挂断的操作
  colse(code, type) {
    console.log(type)
    // 2006 视频播放结束
    // -2301网络断连,且经多次重连抢救无效,更多重试请自行重启播放 
    // -1310 录屏被其他应用打断了
    // 3001 RTMP -DNS解析失败
    // 3002 RTMP服务器连接失败
    // 3003 RTMP服务器握手失败
    // 3005 RTMP 读/写失败,之后会发起网络重试
    let code_list = ['2006', '-2301', '-1310', '3001', '3002', '3003', '3005']
    if (code_list.includes(code)) {
      if (type == 'Pusher') {
        //挂断
        this.hangUp()
      } else {
        //挂断视频、不推流、关闭页面
        //请求后台接口
        this.hangUp()
      }
    }
  },
  //渲染错误事件
  binderrorPusher(errMsg, errCode) {
    // 10001 用户禁止使用摄像头
    // 10002 用户禁止使用录音
    if (errCode == '10001' || errCode == '10002') {
      api.showToast(errMsg)
    }
  },
  //切换大小屏
  bindPusherClick(e) {
    const _class = e.currentTarget.dataset.class
    if (_class == 'small-window' && this.data.is_small == 1) { // 说明pusher是小屏幕
      this.setData({
        is_small: 2
      })
    }
  },
  //切换大小屏
  bindPlayerClick(e) {
    const _class = e.currentTarget.dataset.class
    if (_class == 'small-window' && this.data.is_small == 2) { // 说明player是小屏幕
      this.setData({
        is_small: 1
      })
    }
  },
  //挂断 
  hangUp() {
    console.log('挂断');
    // 停止推流,同时停止摄像头预览
    this.data.pusher.stop({
      success() {
        //挂断视频、不推流、关闭页面
        //dosomething
      },
      fail() {}
    })
  },
  //切换摄像头
  cutCamera() {
    this.data.pusher.switchCamera({
      success() {
        console.log('切换摄像头 success!')
      },
    })
  },
})
//css
.chat{
  position: relative;
  height: 100vh;
  width: 100%;
}
.player,.pusher{
  width: 100% ;height: 100%
}
.small-window{
  position: absolute;
  top: 0px;
  right: 0px;
  width: 150px;
  height: 200px;
  z-index: 100;
}
.fixed{
  position: fixed;
  bottom: 50px;
  left: 50px;
  right: 50px;
  z-index: 101;
  text-align: center;
}
.fixed image{
  width: 50px;
  height: 50px;
  margin: 0px 40px;
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容