微信小程序实现视频会议完整指南:从方案选型到代码落地

微信小程序实现视频会议完整指南:从方案选型到代码落地。本文适用 1v1 视频通话和多人视频会议(团队会议、在线课堂)等场景。

微信小程序实现视频会议完整指南:从方案选型到代码落地

一、前言:小程序做视频会议,先搞清楚这几件事

微信小程序的音视频能力和普通 Web 页面有所区别。浏览器里可以直接用 WebRTC,但小程序运行在微信的沙箱环境中,无法直接使用标准的 WebRTC API 。微信提供了专门的原生组件 live-pusher(推流)和 live-player(拉流)来实现实时音视频,但这两个组件有一个绕不过去的门槛:需要特定类目的小程序才能使用

可使用实时音视频的类目(部分):

  • 社交 > 直播
  • 教育 > 在线教育
  • 医疗 > 互联网医院、在线问诊
  • 金融 > 银行、保险、证券等

更多类目,请查看微信小程序开放的服务类目

如果你的小程序不在这些类目内,live-pusher 和 live-player 组件会直接渲染失败,这是很多开发者踩的第一个坑。

本文适用场景示例:

  • 1v1 视频通话:客服、问诊、在线辅导等
  • 多人视频会议:团队会议、在线课堂等。

二、方案选型:自研 vs 第三方 RTC SDK

2.1 自研方案

自研意味着你需要:

  1. 搭建 WebSocket 信令服务器(处理房间管理、用户进出通知)
  2. 对接 TURN/STUN 服务器(处理 NAT 穿透)
  3. 自行管理推拉流地址(通常是 RTMP 或 WebRTC 地址)

适合场景:有专职音视频团队、对数据私有化要求极高、长期维护成本可控。

2.2 第三方 RTC SDK

对于大多数业务团队,第三方 SDK 是更务实的选择。主流方案对比:

维度即构科技(ZEGO)腾讯云 TRTC阿里云 RTC
小程序原生支持支持支持支持
免费额度每月 10000 分钟每月 10000 分钟少量免费试用
国内平均延迟< 200ms< 300ms200-500ms
接入复杂度

本文选用 ZEGO RTC SDK 为例。原因:小程序端接入步骤简洁,官方文档有完整的微信小程序示例,免费额度对中小项目可以零成本启动。

三、环境准备与资质申请

3.1 微信小程序类目申请

登录微信公众平台,进入 设置 > 基本设置 > 服务类目,添加支持实时音视频的类目。审核通常需要 1-3 个工作日,建议在开发前就提交申请,避免联调阶段被卡住。

3.2 开发环境配置

在 app.json 中声明所需权限:

{
  "permission": {
    "scope.camera": {
      "desc": "视频会议需要使用摄像头"
    },
    "scope.record": {
      "desc": "视频会议需要使用麦克风"
    }
  }
}

在微信公众平台 开发 > 开发管理 > 开发设置 > 服务器域名 中,将 SDK 所需域名加入白名单(具体域名列表参考ZEGO 官方文档)。

3.3 获取 AppID 和 AppSign

前往 ZEGO 控制台注册账号,创建项目后获取 AppID 和 AppSign,后续代码中会用到。

3.4 引入 SDK

npm install zego-express-engine-miniprogram

在微信开发者工具中点击 工具 > 构建 npm,完成后即可在页面中引用。

四、核心实现:1v1 视频通话

4.1 整体架构

用户A ──推流──▶ ZEGO RTC 服务器 ──拉流──▶ 用户B
         ◀──拉流──                ◀──推流──

信令流:用户A/B 通过 SDK 事件感知对方进出房间,动态更新拉流列表

4.2 页面结构(WXML)

<span><</span>!-- meeting.wxml --<span>></span>
<span><</span>view class="container"<span>></span>
  <span><</span>!-- 本地推流(自己的画面) --<span>></span>
  <span><</span>live-pusher
    id="local-pusher"
    url="{{pushUrl}}"
    mode="RTC"
    autopush
    muted="{{isMuted}}"
    enable-camera="{{cameraOn}}"
    class="local-video"
  /<span>></span>

  <span><</span>!-- 远端拉流(对方的画面) --<span>></span>
  <span><</span>live-player
    wx:for="{{remoteStreams}}"
    wx:key="streamID"
    src="{{item.playUrl}}"
    mode="RTC"
    autoplay
    class="remote-video"
  /<span>></span>
<span><</span>/view<span>></span>

4.3 初始化 SDK

// meeting.js
const ZegoExpressEngine = require('../../miniprogram_npm/zego-express-engine-miniprogram/index');

const APP_ID = 123456789;       // 替换为你的 AppID
const APP_SIGN = 'xxxxxxxx...'; // 替换为你的 AppSign

Page({
  data: {
    pushUrl: '',
    remoteStreams: [],
    isMuted: false,
    cameraOn: true,
  },

  onLoad(options) {
    this.roomID  = options.roomID  || 'room_001';
    this.userID  = options.userID  || `user_${Date.now()}`;
    this.streamID = `stream_${this.userID}`;
    this.initEngine();
  },

  initEngine() {
    this.engine = ZegoExpressEngine.createEngine(APP_ID, APP_SIGN, true, {});
    this.registerCallbacks();
    this.joinRoom();
  },

4.4 注册事件回调

registerCallbacks() {
    this.engine.on('roomStreamUpdate', (roomID, updateType, streamList) => {
      if (updateType === 'ADD') {
        const newStreams = streamList.map(stream => ({
          streamID: stream.streamID,
          playUrl: this.engine.startPlayingStream(stream.streamID),
        }));
        this.setData({
          remoteStreams: [...this.data.remoteStreams, ...newStreams],
        });
      } else if (updateType === 'DELETE') {
        const deletedIDs = new Set(streamList.map(s => s.streamID));
        this.setData({
          remoteStreams: this.data.remoteStreams.filter(s => !deletedIDs.has(s.streamID)),
        });
      }
    });

    this.engine.on('roomStateUpdate', (roomID, state, errorCode) => {
      if (state === 'DISCONNECTED' && errorCode !== 0) {
        wx.showToast({ title: '连接断开,请检查网络', icon: 'none' });
      }
    });
  },

4.5 加入房间并推流

  async joinRoom() {
    const token = await this.fetchToken(this.userID, this.roomID);

    await this.engine.loginRoom(this.roomID, token, {
      userID: this.userID,
      userName: `用户${this.userID}`,
    });

    // 拉取已在房间内的流(不会触发 ADD 事件)
    const existingStreams = await this.engine.getRoomStreamList(this.roomID);
    const remoteStreams = existingStreams.map(stream => ({
      streamID: stream.streamID,
      playUrl: this.engine.startPlayingStream(stream.streamID),
    }));
    this.setData({ remoteStreams });

    // 开始推送本地流
    const pushUrl = this.engine.startPublishingStream(this.streamID);
    this.setData({ pushUrl });
  },

  // Token 应由服务端生成,不要在前端硬编码 AppSign
  async fetchToken(userID, roomID) {
    const res = await wx.request({
      url: 'https://your-server.com/api/zego-token',
      method: 'POST',
      data: { userID, roomID },
    });
    return res.data.token;
  },

4.6 退出房间

  onUnload() {
    this.leaveRoom();
  },

  leaveRoom() {
    this.engine.stopPublishingStream();
    this.engine.logoutRoom(this.roomID);
    ZegoExpressEngine.destroyEngine();
  },
});

五、进阶:多人视频会议室

5.1 九宫格 / 演讲者布局

根据在线人数动态切换布局类名:

<span><</span>view class="grid grid-{{remoteStreams.length <= 1 ? 'single' : remoteStreams.length <= 3 ? 'quad' : 'nine'}}"<span>></span>
  <span><</span>live-player
    wx:for="{{remoteStreams}}"
    wx:key="streamID"
    src="{{item.playUrl}}"
    mode="RTC"
    autoplay
  /<span>></span>
<span><</span>/view<span>></span>
/* meeting.wxss */
.grid-single live-player { width: 100%; height: 100%; }
.grid-quad  live-player  { width: 50%;  height: 50%;  float: left; }
.grid-nine  live-player  { width: 33.33%; height: 33.33%; float: left; }

5.2 常用会议控制

  toggleMute() {
    this.setData({ isMuted: !this.data.isMuted });
    // live-pusher 的 muted 属性绑定了 isMuted,自动生效
  },

  toggleCamera() {
    this.setData({ cameraOn: !this.data.cameraOn });
    // live-pusher 的 enable-camera 属性绑定了 cameraOn,自动生效
  },

  switchCamera() {
    wx.createLivePusherContext('local-pusher').switchCamera();
  },

5.3 性能优化建议

人数超过 4 人时,可通过 SDK 接口动态降低非主讲人的分辨率和帧率,减少下行带宽压力:

  setLowQualityForBackground(streamID) {
    // 具体 API 参考 SDK 文档中的 setPlayStreamVideoType
    this.engine.setPlayStreamVideoType(streamID, 'low');
  },

六、常见问题与解决方案

问题现象原因解决方法
live-pusher 不渲染,显示空白小程序类目不支持实时音视频在公众平台申请对应服务类目
推流成功但对方看不到画面SDK 域名未加入白名单在开发设置中配置合法域名
真机黑屏,模拟器正常未申请摄像头/麦克风权限在 app.json 中声明 permission
进入房间看不到已有成员未主动拉取已有流列表loginRoom 后调用 getRoomStreamList
多人时画面卡顿上行带宽不足或码率过高动态降低非主讲人分辨率
iOS 和 Android 画面比例不一致系统渲染差异统一设置 object-fit: fillCrop

七、总结

核心流程三步:申请类目资质 → 初始化 SDK 加入房间 → 用 live-pusher 推流 + live-player 拉流。多人会议在此基础上动态管理流列表即可。

延伸方向:

  • 云端录制:通过 ZEGO 服务端 API 开启混流录制,录制文件存储到 OSS。
  • 美颜滤镜:SDK 内置基础美颜,高级效果可接入第三方美颜 SDK。
微信小程序实现视频会议完整指南:从方案选型到代码落地

原创文章,作者:ZEGO即构科技,如若转载,请注明出处:https://market-blogs.zego.im/reports-baike/3436/

(0)
上一篇 6天前
下一篇 2天前

相关推荐

发表回复

登录后才能评论