VoIP 插件错误码

1. 后台返回错误码

errCode 描述
1 roomId 错误
2 设备 deviceId 错误
3 voip_id 错误
4 voipToken 错误(刷脸模式)
5 生成 voip 房间错误
7 openId 错误
8 openId 未授权(刷脸模式)
9 openId 未授权设备(硬件模式)或不是 userId 联系人(刷脸模式)
12 微信小程序音视频能力审核未完成,正式版中暂时无法使用
13 硬件设备拨打微信,voipToken 错误
14 微信拨打硬件设备,voipToken 错误
15 欠费
17 voipToken 对应 modelId 错误
19 openId 与微信小程序 appId 不匹配(同一个用户在不同微信小程序的 openId 不同)
20 openId 无效
22 传入的 chargeType 非法
23 当前设备 license 已过期
24 当前设备未激活 license

2. 插件内部错误码

以插件最新版本支持为准

errCode 描述
1000 使用 WMPF 注册设备时,deviceToken 获取失败
1001 voipToken 为空或类型错误(仅在要求传入的情况下)
1002 CGI 请求失败
1003 CGI 返回值解析失败
1004 接口调用参数错误
1005 插件当前正在处理其他通话
1006 当前接口必须在 WMPF 中使用
1007 当前接口必须在微信客户端中使用
1008 通话被中断,具体原因需查看 errMsg
1011 当前平台不支持该功能
2000 加入房间失败,具体原因需查看 errMsg
2001 加入房间失败:当前有其他微信小程序 VoIP 通话正在进行
2002 加入房间失败:SDK 重置失败
2003 加入房间失败:SDK 初始化失败
2004 加入房间失败:SDK 加入房间失败
2005 加入房间失败:join 回调失败
2006 加入房间失败:talk 回调失败
2007 加入房间失败:调用音视频设备失败(如无法启用麦克风等)
2008 加入房间失败:获取 sessionKey 失败
2009 加入房间失败:已取消或微信小程序退到后台
2010 加入房间失败:join CGI 请求失败
2100 加入房间失败:当前有其他微信好友 VoIP 或系统电话正在进行
2102 加入房间失败:无访问音视频设备的权限(如录音权限等)
2103 加入房间失败:无调用 JSAPI 的权限
2104 加入房间失败:其他 CGI 异常,具体原因需查看 errMsg

3. Error 类

需要插件 2.4.0 版本开始支持

3.1 VoipError

插件抛出异常的基类,继承自 Error,并增加下列属性:

属性名 类型 简介
errMsg string 错误信息
errCode number 错误码
errno number 基础库接口返回的 errno
cause unknown 如果错误本身是由其他错误引起的,这里包含原始的错误对象

3.2 VoipCgiError

后台请求失败错误,继承自 VoipError,仅作类型区分,没有新增属性。

3.3 VoipJoinError

加入房间失败相关错误,继承自 VoipError,并增加下列属性:

这两个属性开发者一般不需要关注,仅微信侧排查问题需要

属性名 类型 简介
extErrMsg string wx.joinVoipChat 报错里额外的信息
errType number wx.joinVoipChat 报错返回的 errType

3.4 VoipPluginError

插件内其他类型的错误,继承自 VoipError,仅作类型区分,没有新增属性。

校园场景支付刷脸模式

对于部分存量的支付刷脸设备,我们额外支持通过微信支付人脸识别的用户身份来发起通话。

支付刷脸设备的通话存在以下限制:

  • 只支持微信支付刷脸设备使用,具体的开通方式请参考微信支付的相关文档;
  • 只支持安卓设备,WMPF <= 2.0 版本;
  • 只支持设备发起呼叫,不支持手机微信呼叫设备。

1. 发起通话

支付刷脸设备也是通过 initByCaller 接口发起通话,但是在参数上有一些区别。支付刷脸设备请使用下列 businessType:

businessType 业务类型 caller.id listener.id voipToken 最低版本
0 刷脸模式(时长计费) 微信支付刷脸返回的 user_id 微信用户 openId 微信支付刷脸返回的 voip_token
3 刷脸模式(license 计费) 同上 同上 同上 2.3.8
  • 使用前,需要在微信小程序中提前通过 wx.authorize 让 openId 对应的用户授权 scope.voip,否则会返回 errCode: 8,具体流程请参见第 2 节;
  • listener 必须为 caller 联系人。否则会返回 errCode: 9

2. 联系人管理

2.1 联系人绑定

校园场景下,开发者需要接入插件「联系人绑定关系」功能,只有在家长完成关系绑定操作后,孩子才可以给家长拨打音视频电话。

联系人绑定是 userId(孩子)和 openId(家长)的映射关系,开发者还能够通过相关接口实现联系人管理功能。

管理员绑定

第一个联系人为管理员,需要 wx.authorize 授权 voip.scope,再调用插件接口 wmpfVoip.bindVoipAdminContact 完成绑定。

// 绑定成为管理员
try {
  await wx.authorize({
    scope: 'scope.voip',
  })
  const res = await wmpfVoip.bindVoipAdminContact({
    userId: 'xxxxxx', // 传入微信支付刷脸返回的 user_id
  })
  console.log(`bindVoipAdminContact`, res)
} catch (error) {
  console.error('authorize scope voip', error)
  wx.showToast({
    title: '请前往设置页授权通话提醒',
    icon: 'none',
  })
}

其他联系人绑定

其他联系人由管理员分享页面进行绑定。即管理员在微信小程序中点击「分享」按钮, 并通过 wmpfVoip.getBindContactPath 获取插件分享页面链接,并在页面的 onShareAppMessage 中设置。

绑定完成后,会跳转到 setVoipEndPagePath 设置的页面。

// 获取插件联系人绑定关系页面路径,用户会在该页面进行关系绑定操作。
// 分享绑定页面:在学生管理员进入页面时,提前获取分享链接
wmpfVoip
  .getBindContactPath({
    userId: 'xxxx', // 学生 id
    userName: 'xxxx', // 学生名字
    userAvatar: 'xxxx', // 学生头像
  })
  .then(path => {
    // 在 onShareAppMessage 中使用 path
    // 可在 path 后拼上参数, 如'&xxx=yyy', <a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>中通过 getPluginEnterOptions 获取
  })

// Page 中
Page({
  onShareAppMessage() {
    return {
      title: `邀请你成为「${userName}」的联系人`,
      path: 'xxxx', // getBindContactPath 获取的 path
    }
  },
})

// app.js
App({
  onLoad() {
    // 设置联系人绑定关系页面操作完成后要跳转的页面路径。
    wmpfVoip.setVoipEndPagePath({
      key: 'BindContact',
      url: 'xxxxxx',
      options: 'param1=xxx&param2=xxx',
    })
  },
})

注意

  • userName 和 userId(微信支付刷脸返回的 user_id)应与拨打方(学生)信息录入时保持一致;
  • 拨打方应能获取接听方联系人的 openId、名字, 用于显示在联系人列表页面;
  • 在完成绑定过程中,会触发相应事件。可通过 onVoipEvent 进行监听。具体参考 bindContact 的 type。

2.2 查询联系人列表

插件接口

wmpfVoip.getVoipBindContactList({ userId: '学生userId' }).then(list => {
  /**
      [{
        user_id: 'xxx',
        is_ad: 0, // 0 普通联系人,1 管理员
        openid_list: [], // 管理员可以获取孩子(user_id)下绑定的所有联系人(openid)列表,包括自己。
      }]
    */
})

后台接口

请求地址

POST https://api.weixin.qq.com/wxa/business/getvoipcontactlist?access_token=ACCESS_TOKEN

请求参数

属性 类型 必填 说明
access_token / cloudbase_access_token string 接口调用凭证
user_id string 学生 userId

返回值

Object

返回的 JSON 数据包

属性 类型 说明
errcode number 错误码
errmsg string 错误信息
contact_list ContactInfo[] 联系人列表

ContactInfo

属性 类型 说明
open_id string 联系人 openid
role number 联系人身份,0 为普通联系人,1 为管理员。

errCode 合法值

errCode 说明
0 成功
1 用户无联系人列表

2.3 删除联系人

只有管理员才能删除学生(user_id)的联系人(openid),管理员不能删除自己。

wmpfVoip.deleteVoipBindContact({
  userId: 'xxx',
  openidList: ['openId1', 'openId2'],
})

2.4 转移管理员

只有管理员才能调用转移接口。

wmpfVoip.transerAdmin({
  userId: 'xxx',
  newAdminOpenid: '新管理员的openid',
})

2.5 监听事件

在绑定联系人的过程中,onVoipEvent 会收到 bindContact 事件,获取分享绑定联系人状态:

data 参数

属性 类型 说明
type string 绑定结果,取值参见下文
errMsg string 错误信息
userId string 学生 user_id

type 取值

type 描述
success 联系人绑定成功
unbind 联系人未绑定该学生
binded 联系人已绑定过该学生
expire 分享链接过期
overload 该学生绑定的联系人已超过限制
invalid 分享链接非法,原因可能是该链接由非管理员分享
auth 联系人授权 scope.voip 失败
cancel 联系人取消绑定

微信小程序音视频通话插件更新日志

2.4.6

  1. 增加了 camera-device 组件
  2. 优化

2.4.5

  1. 增加了 Voip 视频通话时的微信视频编码参数

2.4.4

  1. 优化

2.4.2 – 2.4.3

  1. 修复:拨打后快速挂断,可能导致后续通话发起失败的问题。

2.4.1

  1. 新增:使用 callDevicecallWMPF 呼叫设备时,可以不需要用户授权。
  2. 修复:部分情况下静音和免提状态错误问题。

2.4.0

  1. 新增:新接口 callDevice 支持微信呼叫 Linux 设备。
  2. 新增:新接口 callWMPF 优化微信呼叫安卓 WMPF 的能力,并支持使用 license 计费。
  3. 优化:梳理和补充各类报错的错误码。
  4. 优化:为避免与设备组混淆,groupId 统一更名为 roomId。groupId 参数作为别名仍保留。
  5. 优化:通话稳定性优化。

2.3.10

  1. 新增:支持设置自定义按钮点击后弹出的半屏高度为 75vh(默认 90vh)。
  2. 修复:调用 onVoipEvent 的返回值无法正常 off 事件监听的问题。
  3. 修复:点击屏幕隐藏按钮后按钮仍能响应点击的问题。
  4. 修复:部分 UI 布局问题。
  5. 优化:通话页面屏蔽分享防止用户误操作。

2.3.9

  1. 新增:视频通话过程中支持点击屏幕隐藏按钮。
  2. 新增:setVoipEndPagePath支持自定义页面跳转方式。
  3. 优化:部分 UI 样式调整。
  4. 优化:通话稳定性优化。

2.3.8

  1. 新增:initByCaller 支持使用 timeLimit 参数设置最大通话时长。
  2. 新增:initByCaller 返回 chargeType 表示当次通话使用时长还是 License 计费。
  3. 新增:支持支付刷脸设备使用 License。
  4. 新增:setUIConfig 支持设置 objectFit。
  5. 优化:设备端如果设置了跳转页面,通话结束后不显示通话详情。
  6. 优化:偶现通话时间显示异常问题。
  7. 优化:通话稳定性优化。

微信小程序摄像头插件

本插件主要用于提供「微信小程序摄像头」的部分基础能力和统一的界面。

插件接入可参考:微信小程序示例代码

设备侧 SDK 的接入与微信小程序 Voip 的纯云接入完全一致,请参考相关文档。

1. 微信小程序引入插件

关于微信小程序插件详细说明请参考微信小程序使用插件文档

在「微信小程序管理后台」添加插件后,使用者还需要要在微信小程序的 app.json 中声明本插件。可以在主包引入,也可以在分包引入。

// 主包引入
{
  "plugins": {
    "wmpf-voip": {
      "version": "latest", // latest 表示自动使用最新版本。也可使用具体版本,如 2.3.8
      "provider": "wxf830863afde621eb"
    }
  }
}
// 分包引入
{
  "subpackages": [
    {
      "root": "xxxx",
      "pages": [],
      "plugins": {
        "wmpf-voip": {
          "version": "latest", // latest 表示自动使用最新版本。也可使用具体版本,如 2.3.8
          "provider": "wxf830863afde621eb"
        }
      }
    }
  ]
}

完成声明后,可以在微信小程序中来确认是否引入成功

const wmpfVoip = requirePlugin('wmpf-voip').default
console.log(wmpfVoip) // 有结果即表示引入插件成功

2. 引入摄像头组件

插件引入成功后,需引入摄像头组件。

页面或者组件的 json 文件中声明:

"usingComponents": {
    "camera-device": "plugin-private://wxf830863afde621eb/publicComponents/camera-device/camera-device"
  },

wxml 中使用组件:传入sn、model-id,device-name

// page.wxml
<camera-device sn="{{sn}}" device-name="{{deviceName}}" model-id="{{modelId}}" class="camera-deivce" bind:changeQuality="changeQuality"></camera-device>

wxss 中自定义 camera-deivce.

// page.wxss
.camera-deivce {
  width: 100vw;
  height: 300px;
}

预期:

3. 组件属性

属性 类型 默认值 必填 说明
sn String 设备
model-id String MP申请的model-id
device-name String 设备名称
video-quality-list String[] [‘360P’, ‘480P’, ‘720P’, ‘1080P’, ‘4K’] 个数不超过 5 个。当用户切换清晰度时,会显示在面板上,切换后会触发组件事件’changeQuality’

事件:切换清晰度 changeQuality

类型 说明
index Number 用户选择的清晰度在传入的video-quality-list数组中的index。

示例:

<camera-device 
    sn="{{sn}}" 
    device-name="{{deviceName}}" 
    model-id="{{modelId}}" 
    class="camera-deivce" 
    bind:changeQuality="changeQuality"
>
</camera-device>
Page({
    changeQuality(res) {
        console.log(res)
    }
})

4. 更新日志

请参考《微信小程序音视频通话插件更新日志》

开发安卓设备端应用

在安卓设备端,开发者需要运行一个安卓应用(文档中也称为微信小程序 Launcher),用来进行设备注册、运行微信小程序进行 VOIP 通话等操作。

1. 接入 WMPF 并运行微信小程序

在安卓平台上,微信小程序视频通话能力是在微信小程序中实现的。需要由设备端运行的安卓应用拉起开发者开发的微信小程序来发起和接听音视频通话。

在完成开发前准备「3.1 接入微信硬件平台」,并进行设备注册后,可以获得 WMPF 运行所需的 productIdkeyVersiondeviceIdsignature 等硬件信息。

请参考WMPF 文档中的说明,完成下列操作:

  • 部署 WMPF Service APK;
  • 参考 WMPF 提供的示例代码在应用中集成 WMPF Client;
  • 调用 activateDevice 完成设备激活;

2. 设备注册

在发起通话前,需要先完成设备注册。请参考设备认证文档完成 RPMBD 服务的部署、SDK 接入和设备注册的步骤。

完成设备注册后,可以在发起通话前通过 SDK 的 getDeviceToken 接口获取设备票据,传递到微信小程序内做为调用 VOIP 插件发起通话的 voipToken 使用。

注意:

  • 此处使用的 SN,必须经过 WMPF 的 addDevice 接口作为 deviceId 注册,并与 WMPF 设备激活 使用的 deviceId 一致。 否则后续无法正常发起通话。
  • 使用设备认证 SDK 时需要保证 rpmbd 服务已经成功运行。

3. 运行微信小程序

完成 WMPF 安装和设备激活后,便可以调用 launchMiniProgram 启动指定的微信小程序。

3.1 安卓应用与微信小程序通信

微信小程序启动后,应用可以通过下列方式和微信小程序内进行通信

  • 简单的「安卓应用 -> 微信小程序」单向单次传递参数的场景,可以直接在启动微信小程序的 path 中拼接 query。
  • 如果安卓应用要接收微信小程序发来的事件、需要双向通信或者数据量大时可以通过 WMPF 提供的通信通道(Invoke Channel)

3.2 运行开发版/体验版微信小程序

在使用开发版/体验版微信小程序进行调试时,请参照下列步骤。

注意:必须扫码登录后才能正常下载开发版/体验版。开发版/体验版更新后,建议重启 WMPF 以更新到最新。

  1. 开发者在开发者工具点击「预览」,将开发版微信小程序代码包上传到后台。也可提交成体验版;
  2. 在 WMPF 上调用 login 弹出扫码登录界面,由在开发者工具上登录的同个微信号扫码登录;
  3. WMPF launchMiniProgram 时指定 appType 为开发版或体验版;
  4. 开发版有有效期,过期后需要重新进行上述步骤

微信小程序音视频通话 SDK (Linux 设备)

此 SDK 运行于搭载 Linux 系统的硬件设备上,为设备提供向手机微信内的微信小程序拨打 VoIP 通话的能力。

使用此 SDK 之前,请先参考《微信小程序音视频通话(for 硬件)》完成前置的接入和微信小程序开发流程。

1. 设备要求

1.1 硬件要求

  • CPU:未定最低要求,推荐双核 500MHz CPU 或更高规格。
  • 内存:推荐 64M+ RAM。SDK 本身至少需要 13M 内存。
  • 磁盘
    • 空间:32M+ ROM (SDK 代码文件本身有 6.5M, 此外还需要预留文件读写空间)。
    • 规格:推荐 EMMC/UFS;若设备包含 RPMB,则必须确保 RPMB 分区未被使用。
  • 麦克风
    • 采样率:需要支持 16000,不支持的需要厂商自行重采样。
    • 编码格式:必须支持 PCM;可选支持 OPUS/G711/G729(软编软解)。
  • 扬声器:
    • 采样率:需要支持 16000,不支持的需要厂商自行重采样。
    • 编码格式:必须支持 PCM;可选支持 OPUS/G711/G729(软编软解)。
  • 摄像头(可选)
    • 分辨率:至少 240×320。
    • 编码格式
      • H264:必须支持, 如果设备确不支持 H264, 你可以选择语音通话。
      • H265:可选支持, 仅微信 8.0.36 及以上版本支持。
      • 暂不考虑支持其它视频编码格式。

RTOS 设备

针对 RTOS 设备,我们推出云对云的方案,也就是云端代理的方案。

云对云方案主要面向无法跑完整 VoIP SDK 的低功耗设备,通过设备商后台转发音视频数据流和信令,打通低功耗设备与微信 VoIP 服务器的通信链路。

呼叫流程:

SDK 下载地址;https://git.weixin.qq.com/wxa_iot/cloudvoipsdk

1. 设备要求

  • HTTPS 通信能力
  • 存储能力
  • 音(视)频能力

2. 设备端开发

在低功耗设备上需要集成 设备端 SDK,用于向手机微信内的微信小程序拨打 VoIP 通话。

设备 SDK 需先使用微信小程序 appid,modelid,设备 id,数据文件夹路径调用 wx_init 函数初始化。

如果设备第一次运行设备 SDK,还需要调用 wx_device_register 函数向微信后台注册当前设备。注册设备时需要微信小程序的 snticket。

如果注册成功,那么此时可以调用 wx_cloudvoip_client_call 函数发起微信 VoIP 通话,需要传入设备要拨打的微信用户的 openid 和音视频云后台识别 VoIP 所需的信息 payload(内容由厂商和云服务器后台自行决定,会被传输给云服务器后台)。如果呼叫成功,说明设备被允许拨打音视频通话给指定的微信用户,厂商此时可以自行创建到厂商的云服务器后台的音视频数据流并开始传输(此步骤也可以在 call 函数之前发起),总之,payload 就是提供给厂商设备到其云服务器通道的标识,方便云服务器接收到微信后台通知后找到设备传输的音视频流。

需要注意,微信用户必须先在厂商的微信小程序内对指定的设备发起通话的权限进行授权,而且未在微信小程序设置内关闭授权或删除微信小程序(清理微信小程序所有数据和授权信息),否则设备将无权发起通话。call 函数将返回没有权限。

3. 接口说明

具体接口如下,使用方法请参考 SDK 包中 example 目录下的 demo 代码。

/**
 * @brief 初始化 voip
 *
 * 调用其它接口之前需要调用 wx_init() 初始化
 *
 *
 * @param stack (nonnull) 开发者实现 TLS 连接、关闭、读、写接口。
 * @param hal (nonnull) 开发者实现 OS 相关的接口
 * @param config (nonnull) 设备配置信息
 * @return
 *  - WXERROR_RESOURCE_EXHAUSTED: 内存资源分配失败
 *  - WXERROR_INVALID_DEVICEID: 设备已注册,但当前 sdk 用的 deviceid、modelid 不对
 */
wx_error_t wx_init(wxvoip_network_https_impl_t *stack, wxvoip_os_impl_t *hal, wx_cloudvoip_config_t *config);

/**
 * @brief 销毁 voip
 * 
 * 进行资源释放
 */
void wx_destory(void);

/**
 * @brief 注册设备
 * 
 * 需要调用一次此接口,若设备已经注册,则立即返回,若设备未注册或注册数据错误,会再注册。
 *
 * @param sn_ticket (nonnull) <a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>对应的 snticket,参考:
 * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/hardware-device/getSnTicket.html
 * @return
 *  - WXERROR_RESOURCE_EXHAUSTED: 内存资源分配失败
 *  - WXERROR_RESPONSE: 后台返回失败,一般是网络问题
 *  - WXERROR_IO: IO 失败,一般是写文件接口返回失败
 *  - 其它:
 *       -10008: snticket 有问题
 */
wx_error_t wx_device_register(const char *sn_ticket);

/**
 * @brief 检测设备是否已经被注册
 * 需要在 wx_init 调用完成之后才能调用本函数.
 *
 * @param is_registered_out 输出设备注册情况.
 * @return 检测过程中是否出现错误, 比如数据文件夹不可读写, 或者 rpmb
 * 设备不能被正常访问.
 *   - WXERROR_OK: 正确返回, 可以正确使用 is_registered_out 的值来判断设备是否注册过
 *   - WXERROR_FAILED_PRECONDITION: wx_init 未被调用
 *   - WXERROR_INVALID_ARGUMENT: 参数为空
 *   - WXERROR_INVALID_DEVICEID: 设备已注册,但当前 sdk 用的 deviceid、modelid 不对
 *   - WXERROR_UNKNOWN: 注册信息被破坏,这种情况下,一般可以清理设备重新注册
 */
wx_error_t wx_device_is_registered(int* is_registered_out);

/**
 * @brief 拨打 VoIP 通话给指定的微信用户
 *
 * 厂商需要自行实现用户的通讯录功能.
 * 厂商需要在自己的微信小程序内提示用户向指定的设备 (设备 ID) 授权 VoIP 通话权限后,
 * 方可在携带该设备 ID 的设备上向该用户拨打 VoIP 通话.
 *
 * @param room_type 通话类型,有音频和音视频两种
 * @param caller 本设备的 VoIP 角色设置.
 * @param callee 要拨打的微信用户的 VoIP 角色设置
 * @param custom_query 微信小程序页面自定义参数, 可以传 NULL
 * @param payload 第三方云 SDK 标记 VoIP 流的标识符(比如 json)
 * @return
 *   - WXERROR_FAILED_PRECONDITION: 不能发起通话请求,比如获取 token 不对等.
 *   - WXERROR_RESOURCE_EXHAUSTED: 内存资源分配失败
 *   - WXERROR_RESPONSE: 后台返回失败,一般是网络问题
 *   - 负数:请求通话时后台返回了失败码取负,可参考https://mp.weixin.qq.com/wxopen/plugindevdoc?appid=wxf830863afde621eb
 *       1  roomid 错误
 *       2  设备 deviceId 错误
 *       3  voip_id 错误
 *       4  校园场景支付刷脸模式,voipToken 错误
 *       5  生成 voip 房间错误
 *       7  openId 错误
 *       8  openId 未授权
 *       9  校园场景支付刷脸模式:openId 不是 userId 的联系人;硬件设备模式:openId 未绑定设备
 *       12 微信小程序音视频能力审核未完成,正式版中暂时无法使用
 *       13 硬件设备拨打手机微信模式,voipToken 错误
 *       14 手机微信拨打硬件设备模式,voipToken 错误
 *       15 音视频费用包欠费
 *       17 voipToken 对应 modelId 错误
 *       19 openId 与微信小程序 appId 不匹配。请注意同一个用户在不同微信小程序的 openId 是不同的
 *       20 openId 无效
 *       10008 snticket 过期
 */
wx_error_t
wx_cloudvoip_client_call(wx_cloudvoip_session_type_t room_type,
                         const wx_cloudvoip_member_t* caller,
                         const wx_cloudvoip_member_t* callee,
                         const char* custom_query,
                         const char* payload);

/**
 * @brief 微信拨打设备,设备使用 roomid 加入通话
 *
 * 厂商需要自行将微信小程序端呼叫设备时的 roomid 流转到设备上.
 *
 * @param roomid 微信小程序端插件发起通话后,获取本次通话的roomid
 * @param payload 第三方云 SDK 标记 VoIP 流的标识符(比如 json)
 * @return
 *   - WXERROR_FAILED_PRECONDITION: 不能发起通话请求,比如获取 token 不对等.
 *   - WXERROR_RESOURCE_EXHAUSTED: 内存资源分配失败
 *   - WXERROR_RESPONSE: 后台返回失败,一般是网络问题
 *   - 负数:请求通话时后台返回了失败码取负,可参考https://developers.weixin.qq.com/miniprogram/dev/framework/device/voip-plugin/api/errCode.html
 *       1  roomid 错误
 *       2  设备 deviceId 错误
 *       3  voip_id 错误
 *       4  校园场景支付刷脸模式,voipToken 错误
 *       5  生成 voip 房间错误
 *       7  openId 错误
 *       8  openId 未授权
 *       9  校园场景支付刷脸模式:openId 不是 userId 的联系人;硬件设备模式:openId 未绑定设备
 *       12 微信小程序音视频能力审核未完成,正式版中暂时无法使用
 *       13 硬件设备拨打手机微信模式,voipToken 错误
 *       14 手机微信拨打硬件设备模式,voipToken 错误
 *       15 音视频费用包欠费
 *       17 voipToken 对应 modelId 错误
 *       19 openId 与微信小程序 appId 不匹配。请注意同一个用户在不同微信小程序的 openId 是不同的
 *       20 openId 无效
 */
wx_error_t 
wx_cloudvoip_client_join(const char* roomid,
                          const char* payload);

云对云服务端

1. 运行环境要求

  • x86_64 运行环境
  • glibc 2.28 或以上
  • libcurl 7.84.0 或以上

2. 服务端消息接收与推送

对指定的 openid 发起呼叫后,若呼叫成功,微信开放平台会按照标准的 消息推送 方法向开发者后台推送消息。 开发者按照消息推送指引配置回调地址,消息加密方式必须选择「安全模式」。

需要注意的是,服务商角色可能需要服务多个微信小程序,可以使用 第三方平台开发。

推送的消息大概如下:

{"ToUserName":"gh_27632a25xxx","Encrypt":"xxxx"}

// Encrypt 解密后的内容,参考字段说明。

开发者后台收到消息通知后,需要及时回复该请求,否则可能触发微信后台的重发,正确的回复格式如下:

{"errcode":0,"errmsg":"ok"}

errcode 为 0 代表回复成功,openid 所在的微信即响铃。若 errcode 为非 0 值,则微信侧不会响铃,此次通话取消。

字段说明:

参数 数据类型 说明
Action String 平台访问开发者后台的事件类型,本接口固定为join_voip_room
RoomId String 本次通话的房间id
SessionKey String 本次通话的 sessionkey
ServerToken String 本次通话的服务端凭证
Payload String 发起通话时第三方传入的自定义payload
ModelId String Modelid
Sn String Sn

内容示例:

{
"ToUserName":"gh_12345678",
"FromUserName":"openid",
"CreateTime":1709621375,
"MsgType":"event",
"Event":"iot_voip_notify",
"Action":"join_voip_room",
"Payload":"hello",
"RoomId":"wxf830863afde621ebWmpfVoip112343434123434",
"SessionKey":"COrwxeDmrJQuEOrwxeDmrJQuGhhrpMKQHttT1pG4eldlqIhi/L2qFpMQkn0iGCpUjLhxpYobRqjPK4DHWCHgRBi123412341234",
"ServerToken":"xyrOs/pwIHEZf3MFNjbhRHkL5XOmGDtW2nkor6EcmRLtHcoI6mP123412341234==",
"ModelId":"fJ4exxxxxxx",
"Sn":"1234"
}

3. 服务端 VOIP SDK

服务端 sdk 是单例模式,即 sdk 同时只能处理一个音视频通话. 由于开发者的服务端往往需要同时处理多个通话,因此云服务器后台接入服务端 sdk 时,需要每次有新请求时创建一个新进程,并在进程内调用服务端 sdk 接口完成本次的音视频通话.

通过消息推送得到此通呼叫的 server_token 与 payload 后,即可使用 sdk 建立通话。

服务端 sdk 需要数据文件夹路径,调用 wx_init 函数完成初始化工作(不需要微信小程序 appid 和 modelid). 然后通过 join 接口加入 VoIP 房间,同时接收设备的音视频数据流,通过服务端 sdk 的相关接口填入数据或收到数据,即可完成设备与微信用户的音视频流通信。

4. 接口说明

具体接口如下,使用方法请参考 sdk 包中 example 目录下的 demo 代码。

wmpf.h

// Copyright (c) 2023, Tencent Inc.
// All rights reserved.
#pragma once

#include "wmpf/macros.h"
#include "wmpf/module.h"
#include "wmpf/operation.h"
#include "wmpf/types.h"

WX_BEGIN_DECLS

typedef enum wx_wxa_flavor {
  WX_WXA_FLAVOR_RELEASE = 0,  // <a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>正式版
  WX_WXA_FLAVOR_DEBUG = 1,    // 微信小程序开发版
  WX_WXA_FLAVOR_DEMO = 2,     // 微信小程序体验版
} wx_wxa_flavor_t;

#define WX_INIT_CONFIG_TAG 0x00001

/**
 * @brief WMPF 初始化结构体
 *
 * 强烈建议你在使用本结构体前 memset 置零, 避免遗忘某个项的设置.
 */
typedef struct wx_init_config {
  wx_struct_t common;

  /**
   * @brief WMPF 日志文件夹
   *
   * 用于存储 WMPF 日志文件.
   *
   * 若此项填 NULL, WMPF 将不输出日志.
   */
  const char* log_dir;

  /**
   * @brief WMPF 数据文件夹
   *
   * 用于存储 WMPF 数据文件.
   *
   * WMPF 数据文件夹内会保存和设备有关的重要的一次性的注册信息,
   * 无法在删除后恢复. 如果你需要清空设备数据,那么你需要备份 WMPF 的数据文件夹.
   */
  const char* data_dir;

  /**
   * @brief 产品 ID.
   *
   * 该项填 0.
   */
  int product_id;

  /**
   * @brief HostAppID
   *
   * 该项填 NULL.
   */
  const char* host_appid;

  /**
   * @brief 设备 ID (SN)
   *
   * 设备的唯一标识符 (唯一序列号), 该项需要由厂商定义.
   */
  const char* device_id;

  /**
   * @brief 设备签名
   *
   * 该项填 NULL.
   */
  const char* device_signature;

  /**
   * @brief 设备签名版本
   *
   * 该项填 0.
   */
  int device_signature_version;

  /**
   * @brief Model ID
   *
   * Model ID 是调用微信小程序设备相关接口的重要凭证.
   * 在设备接入时从【微信小程序管理后台】申请获得的 Model ID.
   *
   * @see
   * https://developers.weixin.qq.com/miniprogram/dev/framework/device/device-access.html
   * https://developers.weixin.qq.com/miniprogram/dev/framework/device/device-message.html
   */
  const char* model_id;

  /**
   * @brief 微信小程序 AppID
   *
   * 微信小程序 AppID 是微信小程序的唯一标识符, 在 VoIP 场景下此项必填.
   * 在 VoIP 发起通话后, 被拨打的微信用户手机将拉起这里被指定的微信小程序用于通话.
   */
  const char* wxa_appid;

  /**
   * @brief 微信小程序版本
   *
   * 对于 VoIP 场景: 要在微信小程序正式版运行微信 VoIP,
   * 需要先提交设备给微信审核通过后才能获得相关权限. 因此在接入调试期间,
   * 你需要使用<a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>开发版/体验版测试 VoIP 功能的可用性.
   *
   * 要设置为开发版还是体验版, 请向你的微信小程序开发同事咨询.
   *
   * 注意, 如果设备开发同事需要使用微信小程序开发版测试 VoIP 功能,
   * 设备开发同事必须先获得微信小程序开发者权限,
   * 并及时扫描微信小程序开发同事的微信开发者工具的真机调试二维码,
   * 提前在本地微信客户端打开一次开发版微信小程序, 否则微信客户端在收到 VoIP
   * 通话强提醒时, 会缺少微信小程序开发版代码包, 导致<a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>启动失败.
   */
  wx_wxa_flavor_t wxa_flavor;

  /**
   * @brief RPMB 设备路径
   *
   * 如果设备有 RPMB 且 Key 处于可写状态时, 应当采用 RPMB 方式初始化 SDK.
   * 并传入 RPMB 设备路径 (一般为 /dev/mmXXXXX)
   */
  const char* rpmb_device;

  /**
   * @brief 是否仅仅支持 H265
   *
   * 如果设备只支持 H265,可以设置此字段为 true.
   * 
   * 如果不设置,微信 voip 后台会自适应,开发者也可以发送 H265 流,但无法决定手机端过来的是什么类型流.
   * 如果设置 true,则发送和接收都为 H265。
   */
  bool h265_only;

  /**
   * @brief SDK 接收微信小程序推流是否为 4:3 的流。
   *
   * true:  SDK 收到流的宽高比 4:3 --- 320x240 480x352 640x480 1280x720 1920x1080
   * false: SDK 收到流的宽高比 3:4 --- 240x320 352x480 480x640 720x1080 1080x1920
   */
  bool video_landscape;

  /**
   * @brief 订阅 SDK 期望收到的分辨率
   *
   * 开发者可以用此配置来订阅一个分辨率的长边值。
   * 此功能主要是针对那些不希望收到可变分辨率视频流的设备,如果不使用此功能,SDK 收到的视频流在不同的网络环境下会有不同的分辨率。
   * 此功能需要同时向微信提交 appid,待开通订阅机制后才生效。
   * 目前这个配置仅支持如下两个值:
   *   320: video_landscape = true 时收到的是 320x240,video_landscape = fale 时收到的是 240x320
   *   640: 不支持 video_landscape = true 模式,video_landscape = fale 时收到的是 480x640
   */
  int subscribe_video_length;

  /**
   * @brief 订阅 SDK 期望收到的流方向
   *
   * 开发者可以用此配置来订阅自己收到的流的方向
   * SDK 默认收到的是逆时针旋转了 90 度的视频流,如果开发者的硬件没有旋转渲染能力,可以使用这个订阅。订阅 0 度流后,对端的微信客户端会对流进行前处理再发出。
   * 此功能 Beta 中,需要微信客户端与插件均支持方可生效.
   * 目前这个配置仅支持如下两个值:
   *   1:   0 度流, 需要配合微信小程序端的 0 度流参数,两者一致后才能收到 0 度流。
   *   其它:旋转流,默认也是旋转流。
   */
  int subscribe_video_rotation;

  /**
   * @brief 订阅 SDK 期望收到的流比例
   *
   * 开发者可以用此配置来订阅自己收到的流的比例
   * 此功能 Beta 中,需要微信客户端与插件均支持方可生效.
   * 定义如下:
   *   75:   宽/高 * 100 = 75, 例如 240x320
   *   133: 宽/高 * 100 = 133, 例如 320x240
   *   50: 宽/高 * 100 = 50, 例如 160x320
   *   200: 宽/高 * 100 = 200, 例如 320x160
   *   ...
   */
  int subscribe_video_ratio;

  /**
   * @brief 订阅 SDK 期望收到流的最大 fps
   *
   * 开发者可以用此配置来订阅自己收到流的最大 fps
   * SDK 默认收到的 fps 最大会涨至 15,通过这个值,可以限定 fps 的最大值。
   * 此功能 Beta 中,需要微信客户端与插件均支持方可生效.
   * 目前这个配置支持如下值:
   *   5 ~ 15:   
   */
  int subscribe_video_maxfps;
} wx_init_config_t;

/**
 * @brief 初始化 WMPF
 *
 * 在执行任意 WMPF 调用之前需要调用 wx_init() 初始化 WMPF.
 * 函数提供设备的基础信息、启动并运行 WMPF.
 *
 * 该函数是异步函数, 会产生网络请求, 请确保在调用本函数时网络通畅.
 *
 * @param config (nonnull) 初始化参数, 包含设备的初始信息
 * @param get_module (nonnull) WMPF 获取厂商提供的接口的回调函数, 实现方法参见
 * wx_get_module_t 的文档
 * @return 运行是否成功
 *   - WXERROR_INVALID_ARGUMENT: 输入参数不合法,或者产品 ID、设备
 * ID、设备签名不匹配
 *   - WXERROR_TIMEOUT: 超时
 *   - WXERROR_RESOURCE_EXHAUSTED: 网络未联通或者本地磁盘写入失败
 *   - WXERROR_FAILED_PRECONDITION: 设备未通过 adddevice 注册, 或者 wx_init
 * 已被调用过了, 或者当前系统时间不正确.
 *   - WXERROR_INTERNAL: 其他错误
 */
WX_API wx_operation_t wx_init(const wx_init_config_t* config,
                              wx_get_module_t get_module);

/**
 * @brief 停止 WMPF
 *
 * 调用该函数之前, 你需要确保创建的 wx_operation, wx_voip_session 等对象都已经被
 * destroy, 否则 SDK 会崩溃.
 *
 * @return 操作是否成功
 */
WX_API wx_error_t wx_stop();

WX_END_DECLS

cloudvoip_server.h

#pragma once

#include "wmpf/macros.h"
#include "wmpf/operation.h"
#include "wmpf/types.h"

WX_BEGIN_DECLS

typedef struct wx_cloudvoip_session* wx_cloudvoip_session_t;

typedef enum wx_cloudvoip_session_type {
  WX_CLOUDVOIP_SESSION_VIDEO = 0,  // 音视频通话
  WX_CLOUDVOIP_SESSION_AUDIO = 1,  // 纯音频通话
} wx_cloudvoip_session_type_t;

typedef enum wx_cloudvoip_session_status {
  WX_CLOUDVOIP_SESSION_IDLE = 0,     // 初始状态
  WX_CLOUDVOIP_SESSION_CALLING = 1,  // 拨打电话中
  WX_CLOUDVOIP_SESSION_TALKING = 2,  // 通话中 (被拨打的微信用户接听了电话)
  WX_CLOUDVOIP_SESSION_REJECTED = 3,  // 被拨打的微信用户拒绝接听电话
  WX_CLOUDVOIP_SESSION_CANCELED = 4,  // 拨打过程中, 设备取消了电话拨打
  WX_CLOUDVOIP_SESSION_HANGUP_BY_CALLER = 5,  // 通话时设备挂断了电话
  WX_CLOUDVOIP_SESSION_HANGUP_BY_CALLEE =
      6,  // 通话时被拨打的微信用户挂断了电话
  WX_CLOUDVOIP_SESSION_ABORTED = 7,  // 发生异常
  WX_CLOUDVOIP_SESSION_BUSY = 8,  // 被拨打的微信用户处于占线状态
  WX_CLOUDVOIP_SESSION_TIMEOUT = 9,  // 超时未接听
} wx_cloudvoip_session_status_t;

typedef enum wx_cloudvoip_hangup_reason {
  WX_CLOUDVOIP_HANGUP_REASON_UNKNOWN = 0,
  WX_CLOUDVOIP_HANGUP_REASON_MANUAL = 1,  // 用户手动挂断/取消

  // 主叫挂断原因
  WX_CLOUDVOIP_HANGUP_REASON_SYSTEM = 6,  // 被系统电话挂断
  WX_CLOUDVOIP_HANGUP_REASON_APP = 7,     // 被其他应用挂断
  WX_CLOUDVOIP_HANGUP_REASON_DEVICE = 8,  // 采集播放设备启动失败

  WX_CLOUDVOIP_HANGUP_REASON_TIMEOUT = 10, // 超时挂断
  WX_CLOUDVOIP_HANGUP_REASON_REJECT = 11, // 拒绝通话,指的是没有进入通话即挂断
} wx_cloudvoip_hangup_reason_t;

#define WX_CLOUDVOIP_SESSION_LISTENER_TAG 0xF00001

typedef struct wx_cloudvoip_session_listener {
  wx_struct_t common;

  /**
   * @brief 当前 VoIP 通话状态改变
   *
   */
  void (*status)(wx_cloudvoip_session_t session,
                 void* user_data,
                 wx_cloudvoip_session_status_t);

} wx_cloudvoip_session_listener_t;

/**
 * @brief 服务端侧加入 VoIP 房间并创建 VoIP 会话对象
 *
 * 需要注意:
 * 此接口用于设备端 SDK 发起的通话加入。
 * 服务端 SDK 是单例模式的,你不能在单进程内同时创建两个或以上的
 * wx_cloudvoip_session 实例. 因此你需要使用多进程模式来调用服务端 SDK.
 * 因此此处可能还涉及 IPC 需要厂商云自行实现.
 *
 * 发起 VoIP 通话流程:
 * 1. 设备端 SDK 调用 call 方法发起通话
 * 2. 微信后台请求厂商云后台,通知云后台有新的 VoIP 会话已经发起
 * 3. 云后台调用服务端 SDK 的 wx_cloudvoip_session_join 方法加入 VoIP 会话
 * 4. 云后台通过 audio_module, camera_module 传输音视频流给服务端 SDK
 * 5. 服务端 SDK 将接收到的音视频流发送给微信后台,继而发送到用户手机微信中
 *
 * @param listener 会话状态回调
 * @param user_data 回调用户数据
 * @param wxa_appid 微信小程序 AppId
 * @param device_id 设备 SN
 * @param model_id Model ID
 * @param server_token 微信后台向云后台通知 VoIP 会话发起时携带的值
 * @param payload 设备端调用 wx_cloudvoip_client_call 函数传入的 payload
 * @param session_out VoIP 会话结果
 * @return
 *   - WXERROR_INVALID_ARGUMENT: 参数错误
 *   - WXERROR_FAILED_PRECONDITION: wx_init 未调用或未完成.
 */
WX_API wx_operation_t
wx_cloudvoip_session_join(wx_cloudvoip_session_type_t,
                          const struct wx_cloudvoip_session_listener* listener,
                          void* user_data,
                          const char* wxa_appid,
                          const char* device_id,
                          const char* model_id,
                          const char* server_token,
                          const char* payload,
                          wx_cloudvoip_session_t* session_out);

/**
 * @brief 呼叫成功后挂断 VoIP 通话
 *
 * 由于设备端 SDK 不提供挂断方法,云对云场景下,由厂商云后台调用服务端 SDK
 * 的挂断函数挂断 VoIP 通话.
 *
 * @param session 要挂断的 VoIP 会话
 * @param reason 挂断 VoIP 通话的原因
 * @return 操作是否成功
 */
WX_API wx_operation_t
wx_cloudvoip_session_hangup(wx_cloudvoip_session_t session,
                            wx_cloudvoip_hangup_reason_t reason);

/**
 * @brief 作为接听方(微信小程序呼叫设备)挂断 VoIP, 如果是设备呼叫微信小程序,则不建议使用此接口。
 *
 * 微信小程序呼设备的场景,设备端挂断 voip 接口,如果 session 已创建,则需要使用 wx_cloudvoip_session_hangup 挂断
 *
 * @param wxa_appid 微信小程序 AppId
 * @param device_id 设备 SN
 * @param model_id Model ID
 * @param server_token 微信后台向云后台通知 VoIP 会话发起时携带的值
 * @param payload 设备端调用 wx_cloudvoip_client_join 函数传入的 payload
 * @return 操作是否成功
 */
WX_API wx_operation_t wx_cloudvoip_listener_hangup(
    const char* wxa_appid,
    const char* device_id,
    const char* model_id,
    const char* server_token,
    const char* payload,
    wx_cloudvoip_hangup_reason_t reason);

/**
 * @brief 销毁 VoIP 会话对象
 *
 * @param session
 * @return WX_API
 */
WX_API void wx_cloudvoip_session_destroy(wx_cloudvoip_session_t session);

/**
 * @brief 得到当前 session 的 roomid
 *
 *  roomid: voip 通话所在房间的标识。
 * 
 * @param session
 * @return roomid 字符串 或 "".
 */
WX_API const char* wx_cloudvoip_session_get_roomid(wx_cloudvoip_session_t session);

/**
 * @brief 设置对端(微信)的带宽以强制流控,设置后对端的上行码率会在这个值上下波动
 * 
 * @param session
 * @param bandwidth 码率,单位为 kbps. 比如 100,则码率为 100 kbps
 * @return
 *  WXERROR_OK: 操作成功
 *  WXERROR_FAILED_PRECONDITION: session 不满足条件
 *  WXERROR_INTERNAL: 内部错误
 */
WX_API wx_error_t wx_cloudvoip_session_set_remote_bandwidth(wx_cloudvoip_session_t session, int bandwidth);

/**
 * @brief 服务端侧加入 VoIP 房间并创建 VoIP 会话对象
 *
 * 需要注意:
* 此接口用于服务端请求 https://api.weixin.qq.com/wxa/business/iot/voip/call 发起的通话加入
 * 服务端 SDK 是单例模式的,你不能在单进程内同时创建两个或以上的
 * wx_cloudvoip_session 实例. 因此你需要使用多进程模式来调用服务端 SDK.
 * 因此此处可能还涉及 IPC 需要厂商云自行实现.
 *
 * 发起 VoIP 通话流程:
 * 1. 服务端请求 https://api.weixin.qq.com/wxa/business/iot/voip/call 发起通话
 * 2. 微信后台请求厂商云后台,通知云后台有新的 VoIP 会话已经发起
 * 3. 云后台调用服务端 SDK 的 wx_cloudvoip_session_cloud_call_join 方法加入 VoIP 会话
 * 4. 云后台通过 audio_module, camera_module 传输音视频流给服务端 SDK
 * 5. 服务端 SDK 将接收到的音视频流发送给微信后台,继而发送到用户手机微信中
 *
 * @param listener 会话状态回调
 * @param user_data 回调用户数据
 * @param wxa_appid 微信小程序 AppId
 * @param server_token 微信后台向云后台通知 VoIP 会话发起时携带的值
 * @param roomid 房间id
 * @param session_key 房间session_key
 * @param payload 设备端调用 wx_cloudvoip_client_call 函数传入的 payload
 * @param session_out VoIP 会话结果
 * @return
 *   - WXERROR_INVALID_ARGUMENT: 参数错误
 *   - WXERROR_FAILED_PRECONDITION: wx_init 未调用或未完成.
 */
WX_API wx_operation_t
wx_cloudvoip_session_cloud_call_join(wx_cloudvoip_session_type_t,
                          const struct wx_cloudvoip_session_listener* listener,
                          void* user_data,
                          const char* wxa_appid,
                          const char* server_token,
                          const char* roomid,
                          const char* session_key,
                          const char* payload,
                          wx_cloudvoip_session_t* session_out);

WX_END_DECLS

VoIP 视频流指南

微信小程序 Voip 的视频流默认为 H264 编码流,从设备端看,分为设备上行流与微信下行流,分辨率目前最大为 640×480。

对于上行流,是设备采集 -> 设备编码 -> 网络传输 -> 微信接收 -> 微信解码 -> 微信小程序渲染。 对于下行流,是微信采集 -> 微信编码 -> 网络传输 -> 设备接收 -> 设备解码 -> 设备渲染。

需要注意的是,微信客户端使用摄像头采集视频流的原始分辨率为 640×480,并且它的方向与物理方向并不一致,所以针对 RTOS 和 Linux 设备,它们收到流后,需要进行后处理才能渲染正确。

1. Android 设备

Android 设备的开发者无需关心视频流的方向问题。WMPF 会对接收的视频流进行旋转,微信小程序渲染输出即可(objectFit 为 fill)。

2. Linux/RTOS 设备

此类型设备无法运行 WMPF,直接使用 SDK 进行视频流的接收和发送。

2.1 设备发送视频流

手机摄像头的本地采集为上图所示的逆时针旋转 90 度方向,但嵌入式设备一般不会有这种情况,所以当嵌入式设备上的摄像头采集流直接使用 SDK 发送出去后,微信端微信小程序 Voip 插件在不使用 setUIConfig 配置旋转时,反而会渲染出旋转的画面。

有两种方案解决:

  • 旋转嵌入式设备摄像头的视频流为逆时针旋转 90 的视频流。
  • 微信小程序上使用插件的 setUIConfig 配置对方的旋转方向为 270。

SDK 端如何发送视频流才能使微信小程序端尽早的展示图像?

  • 需要在开发者实现的 camera_device->open_stream 调用完成后,才能发送 H264/H265 流。

  • 保证前几桢发送的为信息桢及关键桢,顺序应该如下:

    sps -> pps -> i-frame -> p-frame -> p-frame -> ……

  • 不要使用带有 B 桢的流

  • 需要在状态为 TALKING 时再开始发送,不然前面的信息桢可能会被抛弃导致 p-frame 发送失败,只有等下一个周期的信息桢才能成功。

需要注意的是,设备发送的视频流分辨率,长、宽的数值都需要与 8 对齐

2.2 设备接收视频流

设备接收到的是微信客户端利用摄像头采集并编码的视频流,Android 和 IOS 的摄像头采集时,原始流都是逆时针旋转 90 的流,设备接收到的正是如此。

有两种方案解决:

  • 推荐: 嵌入式设备在收到流后,在输出时顺时针旋转 90 即可。
  • 使用 >= 8.0.54 版本的微信客户端,使用 >= 2.4.5 版本的前端 VoIP 插件,使用支持详细订阅参数的 SDK 版本。详细方法见 2.4

2.3 视频流分辨率

微信小程序 Voip 视频的分辨率不是固定的,它会随着网络质量的变化而变化。

  • 设备上行视频流:分辨率不应该超过 1080p。通话过程中允许分辨率发生变化,但需要及时的发送 SPS 、I 桢等。典型的分辨率是 320×240、640×480。
  • 微信下行视频流:微信下行视频流都是从 320×240/240×320 分辨率开始的,然后根据网络情况,会提升至 640×480/480×640。

针对微信下行视频流,有些 RTOS 设备不支持处理分辨率变化流的能力。开发者可以使用 2.4 的方法固定分辨率。

2.4 微信视频编码定制方法

针对 RTOS 或 Linux 设备,当设备对于微信发过来的 h264/h265 码流要求比较苛刻时,可以使用如下方法来定制微信编码行为,从而获取自定义的视频流。

我们需要了解四个参数:

  • encodeVideoFixedLength 编码的长边值固定起来,不要随着网络质量变化而变化,可取 320、480、640
  • encodeVideoRotation 编码的视频旋转方向。1: 发出正向流. 2: 保持发出旋转流
  • encodeVideoRatio 视频的比例, 宽/高*100
  • encodeVideoMaxFPS 视频的最大 FPS。最小 8,最大 15.

这些参数需要在两个地方同时设置:

  • 微信端:当前端 VoIP 插件版本 >= 2.4.5 时,插件会从 query 参数中解析出这些字段设置到微信里。不管哪种形式的呼叫接口里都有 query 参数,在 query 里正确设置好这些字段即可。
  • SDK 端:使用支持这些参数的 SDK 版本并正确设置。SDK 端的设置入口为 wx_init_config_t 中的 subscribe_video_length、subscribe_video_rotation、subscribe_video_ratio、subscribe_video_maxfps

我们看一个案例, 某设备希望收到 240×320 的正常方向流

参数 取值 说明
encodeVideoFixedLength 320 240×320 的长边为 320
encodeVideoRotation 1 1 为正常方向,2 为旋转方向
encodeVideoRatio 75 宽/高*100 = 240/320 * 100 = 75
  • 在呼叫时,可以传 query 这样的参数:
encodeVideoFixedLength=320&encodeVideoRotation=1&encodeVideoRatio=75
  • SDK 端,给 config.subscribe_video_length、config.subscribe_video_rotation、config.subscribe_video_ratio 也传入对应的值。
  • 前端微信小程序建议做一个微信版本判断的逻辑,大于 8.0.54 才能使用此功能。

其它的分辨率也能定制出来,但需要注意的是,微信小程序 VoIP 场景的微信摄像头原始采集分辨率为 640×480 且为物理方向逆时针旋转 90 的流,这意味着,你不能定制出 640×480 的正常方向流,而只能定制出 480×640 的正常方向流。

2.5 性能参考

2.5.1 微信响铃延时

  • wx_cloudvoip_client_call 接口正常需要 600ms 左右。如果在 RTOS 设备上运行,根据不同的网络侧实现,接口的返回时间会有所不同。
  • wx_cloudvoip_client_call 接口返回成功后,微信侧即响铃。当然,对于不同的手机,延时情况会不一样。对于 iphone,还依赖于 apple 的消息推送,对于 android,各种系统设置还可能导致手机消息异常,可参考通话提醒异常排查指南

2.5.2 视频流延时

以下几种场景下的延时与网络质量有很大关系,以下数据都基于网络正常的情况。

  • 接听成功后,设备端首次收到视频流的时延约 1s ~ 2s 左右,对端不同性能的手机、不同的微信版本,时间会有少许差异。

  • 接听成功后,手机端首次收到视频流的时延约 1s ~ 2s 左右。需要注意的是,手机端如果从悬浮通知直接接听,会经历(1)打开微信小程序与(2)加入通话 两个过程,这里的 1s ~ 2s 指的是(2)加入通话这个过程。打开微信小程序的耗时与手机性能强相关。

  • 典型的场景,设备采集视频 -> 传送到云端 -> 送入云端 SDK 接口 -> 微信小程序端出图: 整个过程的延时大约在 200 ~ 300ms。

异步接口使用指南

头文件:wmpf/operation.h

微信小程序 SDK 的接口存在大量异步操作。如果看到一个函数的返回值类型为wx_operation_t,则说明此函数是一个异步函数,需要在合适的时机调用如下任一函数以等待异步执行的结果:

  • wx_operation_wait():同步阻塞式等待。支持设置超时时间。
  • wx_operation_await():异步等待,不会阻塞当前线程。支持设置回调函数。

如果不关心异步操作的结果,可以调用如下函数释放wx_operation_t对象。

  • wx_operation_destroy():主动销毁wx_operation_t对象。异步操作会继续执行。

如果想取消一个异步操作,可以调用如下函数:

  • wx_operation_cancel():取消异步操作。

请特别注意wx_operation_t对象的生命周期,以免造成内存泄漏:

  • 调用 wx_operation_waitwx_operation_await 函数直至异步调用结束,那么wx_operation_t对象会被自动回收。
  • 如果调用 wx_operation_wait 超时,可以重新调用 wx_operation_wait函数以继续等待,或者调用wx_operation_cancel()主动结束此次异步调用。cancel 后wx_operation_t对象亦会被自动回收。
  • 也可以手动调用wx_operation_destroy()销毁 operation 对象,这种情况下异步操作还会继续进行,但是将不会收到异步调用的结果。

1. 同步等待

同步阻塞当前线程,直到操作完成或超时。

  • 如果操作完成(包括正常返回和接口异常的情况),wx_operation_t 将被自动释放。
  • 如果异步操作没有在指定时间内完成,返回 WXERROR_TIMEOUT。可以有以下选择:
    • 重新调用 wx_operation_wait() 继续等待;
    • 调用 wx_operation_cancel() 取消异步调用;
    • 调用 wx_operation_destroy() 销毁 operation 对象。