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 对象。

硬件抽象层

微信小程序 SDK 正常工作依赖硬件设备平台提供的软硬件接口,包括麦克风、扬声器、摄像头、加密库等模块。我们设计了硬件抽象层,提供一致的接口定义,具体的接口实现需要微信小程序 SDK 的接入方提供。

1. Module 抽象

头文件:wmpf/module.h

我们把每个模块抽象成一个wx_module类型,目前有如下几种 module:

类型 头文件 说明
wx_audio_module wmpf/hardware/audio.h 音频模块,用于从麦克风和扬声器输入/输出音频流
wx_camera_module wmpf/hardware/camera.h 摄像头模块,用于从摄像头获取视频流
wx_crypto_module wmpf/crypto.h 签名算法模块。微信小程序 SDK 依赖系统的加密库中的签名算法。
wx_video_module wmpf/hardware/video.h 视频模块,用于创建远端视频流

当按需实现这几种 Module 后,在调用 wx_init() 初始化微信小程序 SDK 的时候,将使用如下函数指针把 Module 提供给微信小程序 SDK 使用:

typedef wx_error_t (*wx_get_module_t)(const char* id,
                                      struct wx_module** module);

微信小程序 SDK 调用实现的函数时,会传入一个 ID,需要返回此 ID 所对应的 wx_module 的实例。

参数

属性 类型 说明
id char* module ID,在后续定义
module struct wx_module** 对应的 module 实例

当前支持的 module ID 已经预先定义在各个头文件中:

ID 头文件
WX_AUDIO_MODULE_ID wmpf/hardware/audio.h
WX_CAMERA_MODULE_ID wmpf/hardware/camera.h
WX_CRYPTO_MODULE_ID wmpf/crypto.h
WX_VIDEO_MODULE_ID wmpf/hardware/video.h

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断请求的 module 是否有效。

例如一台设备不具备摄像头,那么当微信小程序 SDK 请求 WX_CAMERA_MODULE_ID 时,只需返回 WXERROR_UNIMPLEMENTED

错误码 说明
WXERROR_OK 请求的 module 有效。
WXERROR_UNIMPLEMENTED 请求的 module 无效。

示例代码

// 假设已经实现了如下module:
extern struct wx_audio_module audio_module;
extern struct wx_camera_module camera_module;
extern struct wx_crypto_module crypto_module;
// 现实现HAL获取Module的函数如下:
wx_error_t hal_get_module(const char* id, struct wx_module** module_out) {
  if (!strcmp(id, WX_AUDIO_MODULE_ID)) {
    *module_out = (struct wx_module*)&audio_module;
    return WXERROR_OK;
  } else if (!strcmp(id, WX_CAMERA_MODULE_ID)) {
    *module_out = (struct wx_module*)&camera_module;
    return WXERROR_OK;
  } else if (!strcmp(id, WX_CRYPTO_MODULE_ID)) {
    *module_out = (struct wx_module*)&crypto_module;
    return WXERROR_OK;
  }
  // 假设当前设备没有屏幕,因此不需提供 wx_video_module,那么就返回 WXERROR_UNIMPLEMENTED
  return WXERROR_UNIMPLEMENTED;
}

2. 音频模块

头文件:wmpf/hardware/audio.h

音频模块,需要配置一个 wx_audio_module 类型的实例。该类型主要包含如下几个函数指针,用来获取音频输入/输出设备的信息,或操作音频设备。

运行时,VoIP 将顺序调用这 4 个函数,完成对音频输入/输出设备的配置。

2.1 get_number_of_devices

获取指定类型音频输入/输出设备的数量。

wx_error_t (*get_number_of_devices)(struct wx_audio_module* module,
                                    wx_audio_device_type_t device_type,
                                    size_t* num_devices_out);

参数

属性 类型 说明
module struct wx_audio_module* context
device_type wx_audio_device_type_t 设备类型,可选值
WX_AUDIO_DEVICE_IN(音频输入设备,如麦克风)
WX_AUDIO_DEVICE_OUT(音频输出设备,如扬声器)
num_devices_out size_t* 返回对应类型的音频设备的数量

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备数量是否有效

错误码 说明
WXERROR_OK 成功
WXERROR_INVALID_ARGUMENT num_devices_out 为空

2.2 get_device_name

查询指定类型(如 WX_AUDIO_DEVICE_IN 表示麦克风)音频输入/输出设备列表中第 index 个设备的 ID

wx_error_t (*get_device_name)(struct wx_audio_module* module,
                              size_t index,
                              wx_audio_device_type_t device_type,
                              char** device_name_out);

参数

属性 类型 说明
module struct wx_audio_module* context
index size_t 设备列表中指定的设备。取值范围[0, num_devices_out)
device_type wx_audio_device_type_t 设备类型,同 get_number_of_devices
device_name_out char** 返回对应设备的 ID

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备 ID 是否有效

错误码 说明
WXERROR_OK 成功
WXERROR_INVALID_ARGUMENT num_devices_out 为空
WXERROR_OUT_OF_RANGE index 超出范围

2.3 get_device_info

查询指定类型和设备 ID 的设备的详细信息。该函数用于返回一些在不用打开音频设备的情况下就能获取到的音频设备信息,如支持的音频编码格式。

wx_error_t (*get_device_info)(struct wx_audio_module* module,
                              const char* id,
                              wx_audio_device_type_t device_type,
                              const struct wx_metadata** metadata_out);

参数

属性 类型 说明
module struct wx_audio_module* 上下文
id const char* get_device_name 查到的设备 ID,或者宏 WX_AUDIO_DEVICE_PRIMARY 表示任意有效的设备。
device_type wx_audio_device_type_t get_number_of_devices 的 device_type
metadata_out const struct wx_metadata** 返回对应设备的详情。二级指针指向的对象可以是局部变量。

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备详情是否有效

错误码 说明
WXERROR_OK 成功

2.4 open

调用指定的音频输入/输出设备。

wx_error_t (*open)(struct wx_audio_module* module,
                   const char* id,
                   wx_audio_device_type_t device_type,
                   struct wx_audio_device** device_out);

参数

属性 类型 说明
module struct wx_audio_module* 上下文
id const char* get_device_infoid
device_type wx_audio_device_type_t get_number_of_devices 的 device_type
device_out struct wx_audio_device** 返回对应的设备实例。详见后续 wx_audio_device 类型说明

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备实例是否有效

错误码 说明
WXERROR_OK 成功

2.5 wx_audio_device

属性 类型 说明
common wx_device 设备类型的公共基类。
metadata wx_metadata 对于设备的描述信息。

struct wx_audio_device 本身的功能不多,主要是对于设备的描述。真正需要重点关注的是它的两个拓展类型:wx_audio_device_inwx_audio_device_out

2.5.1 wx_audio_device_in

对于输入设备(如:麦克风)的抽象。这个类型的关键是需要提供 open_input_stream 函数实现,运行时微信小程序 SDK 将调用此函数,创建音频输入流对象 wx_audio_stream_in。微信小程序 SDK 使用 wx_audio_stream_in 对象的实例进一步控制音频采集。

麦克风设备在 VoIP 通话拨通时被创建,VoIP 通话结束时销毁。

属性 类型 说明
device wx_audio_device 基类。
open_input_stream 函数指针 微信小程序 SDK 使用这个函数指针打开/使用麦克风设备。
open_input_stream 的参数
参数 类型 说明
dev wx_audio_device_in * 上下文
config wx_audio_config * 微信小程序 SDK 希望的麦克风配置。
listener wx_audio_stream_in_listener * 微信小程序 SDK 监听麦克风事件的回调函数。开发者需要在合适的时机正确地调用这些函数。
user_data void * 微信小程序 SDK 所依赖的其他上下文信息。调用麦克风事件回调函数时需要带上这个指针。
stream_out wx_audio_stream_in ** 返回新创建的音频输入流对象。定义如下。
wx_audio_stream_in
参数 类型 说明
common wx_audio_stream * 上下文
pause 函数指针 微信小程序 SDK 调用此函数以暂停音频数据输入。
resume 函数指针 微信小程序 SDK 调用此函数以恢复音频数据输入。
wx_audio_stream_in_listener
参数 类型 说明
common wx_struct 基类
data 函数指针 系统调用此函数向微信小程序 SDK 提供麦克风采集到的音频数据。
error 函数指针 系统通过调用此函数告知微信小程序 SDK 麦克风采集音频数据出错。

注意事项:

  1. 微信小程序 SDK 目前仅采集 PCM 格式的音频数据,远期可能增加其他音频格式。
  2. config 目前以采样率 16000、采样长度 20ms、16bit 为默认配置,可能会动态调整。如果设备不支持这些音频配置,设备厂商应当自行对 PCM 数据进行变换。
  3. data 回调可以理解为非阻塞的,即与网络无关。
  4. data 回调提供的一帧音频数据,需要根据 config 设置的参数提供。举例来说:PCM 采样率 16000、采样格式 16Bit、采样长度 20ms 的单通道音频数据,包含 16000 / 1000 * 20 * 1 * (16 / 8) 字节的数据(采样率 / 1000 * 采样长度 * 通道数 * 采样格式 / 8Bit)。

2.5.2 wx_audio_device_out 类型

对于扬声器设备的抽象。这个类型的关键是提供 open_output_stream 函数实现,运行时微信小程序 SDK 将调用此函数,创建音频输出流对象 wx_audio_stream_out。微信小程序 SDK 使用 wx_audio_stream_out 对象的实例进一步控制音频播放。

扬声器设备在 VoIP 通话开始拨打时被创建,先播放铃声,在 VoIP 通话开始时开始播放语音,VoIP 通话结束时销毁。

属性 类型 说明
device wx_audio_device 基类。
open_output_stream 函数指针 微信小程序 SDK 使用这个函数指针打开/使用扬声器设备。
open_output_stream 的参数:
参数 类型 说明
dev wx_audio_device_out * 上下文
config wx_audio_config * 微信小程序 SDK 希望的扬声器配置。
listener wx_audio_stream_out_listener * 微信小程序 SDK 监听扬声器事件的回调函数。应在合适的时机正确地调用这些函数。
user_data void * 微信小程序 SDK 所依赖的其他上下文信息。调用扬声器事件回调函数时需要带上这个指针。
stream_out wx_audio_stream_out ** 返回新创建的音频输出流对象。定义如下。
wx_audio_stream_out 类型
参数 类型 说明
common wx_audio_stream * 上下文
pause 函数指针 微信小程序 SDK 调用此函数以暂停音频数据播放。
resume 函数指针 微信小程序 SDK 调用此函数以恢复音频数据播放。
flush 函数指针 微信小程序 SDK 调用此函数要求音频播放缓存,立刻播放缓存内的音频数据。
wx_audio_stream_out_listener 类型
参数 类型 说明
common wx_struct 基类
data 函数指针 系统通过调用此函数向微信小程序 SDK 拉取待播放(铃声或收到的语音流)的音频数据。
error 函数指针 系统通过调用此函数通知微信小程序 SDK 音频播放出错。

注意事项:

  1. 微信小程序 SDK 目前仅提供 PCM 格式的音频数据,远期可能增加其他音频格式。
  2. config 目前以采样率 16000、采样长度 20ms、16bit 为默认配置,可能会动态调整。如果设备不支持这些音频配置,设备厂商应当自行对 PCM 数据进行变换。
  3. data 回调可以理解为非阻塞的,即与网络无关。
  4. data 回调传入的 buffer 需要足够大,能存下 config 设置的一帧音频数据。举例来说:PCM 采样率 16000、采样格式 16Bit、采样长度 20ms 的单通道音频数据,包含 16000 / 1000 _ 20 _ 1 _ (16 / 8) 字节的数据(采样率 / 1000 _ 采样长度 _ 通道数 _ 采样格式 / 8Bit)。

3. 签名算法模块

头文件:wmpf/crypto.h

微信小程序 SDK 依赖系统内置的签名算法,我们提供了 OpenSSL、MbedTLS、WolfSSL 的 Demo 实现,存放在 example 目录中。如果使用上述 crypto 实现,通常可以直接使用 example 的代码实现,否则请参考 example 实现进行适配。

Demo 实现:

  • example/crypto_mbedtls.c
  • example/crypto_openssl.c
  • example/crypto_wolfssl.c

4. (可选)摄像头模块

头文件:wmpf/hardware/camera.h

摄像头模块,需要配置一个 wx_camera_module 类型的实例。该类型主要是需要你提供如下几个函数指针,用来利用设备提供的摄像头设备对象生成设备发送到手机的视频流

运行时,微信小程序 SDK 将顺序调用这 3 个函数,完成对摄像头设备的配置。

4.1 get_number_of_devices

获取摄像头设备的数量。

wx_error_t (*get_number_of_devices)(struct wx_camera_module* module,
                                    size_t* num_devices_out);

参数

属性 类型 说明
module struct wx_camera_module* 上下文
num_devices_out size_t* 返回对应类型的摄像头设备的数量

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备数量是否有效

错误码 说明
WXERROR_OK 成功
WXERROR_INVALID_ARGUMENT num_devices_out 为空

4.2 get_device_info

查询指定摄像头设备的详细信息。

wx_error_t (*get_device_info)(struct wx_camera_module* module,
                              size_t index,
                              struct wx_camera_device_info* device_info);

参数

属性 类型 说明
module struct wx_camera_module* 上下文
index size_t 设备列表中指定的设备。取值范围[0, num_devices_out)
device_info struct wx_camera_device_info* 微信小程序 SDK 会传入此 device_info,需要填充此结构体的内容。

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备详情是否有效

错误码 说明
WXERROR_OK 请求到的设备详情有效。

4.3 open

调用指定的摄像头设备。

wx_error_t (*open)(struct wx_camera_module* module,
                   const char* id,
                   struct wx_camera_device** device_out);

参数

属性 类型 说明
module struct wx_camera_module* 上下文
id char * get_device_info 查到的设备 ID,或者宏 WX_CAMERA_DEVICE_PRIMARY 表示任意有效的设备。
device_out struct wx_camera_device ** 返回对应的设备实例。

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断得到的设备实例是否有效

错误码 说明
WXERROR_OK 成功

4.4 wx_camera_device

对于摄像头设备的抽象。这个类型的关键是需要提供 open_stream 函数实现,运行时微信小程序 SDK 将调用此函数,创建视频输入流对象 wx_camera_stream。微信小程序 SDK 使用 wx_camera_stream 对象的实例进一步控制视频采集。

属性 类型 说明
common wx_device 设备类型的公共基类。
metadata wx_metadata 对于设备的描述信息。
open_stream 函数指针 微信小程序 SDK 使用这个函数指针打开/使用摄像头设备。

open_stream 的参数:

参数 类型 说明
dev wx_camera_device * 上下文
config wx_camera_stream_config * 微信小程序 SDK 需要的摄像头配置,包括数据流类型。
listener wx_camera_stream_listener * 微信小程序 SDK 监听摄像头事件的回调函数。应在合适的时机正确地调用这些函数。
user_data void * 微信小程序 SDK 所依赖的其他上下文信息。调用摄像头事件回调函数时需要带上这个指针。
stream_out wx_camera_stream ** 返回新创建的视频输入流对象。定义如下。

wx_camera_stream 类型

参数 类型 说明
common wx_struct 基类
update 函数指针 微信小程序 SDK 调用此函数以更新摄像头录制参数。
make_i_frame 函数指针 微信小程序 SDK 调用此函数以请求立刻收到一个 H264/H265 I 帧。

wx_camera_stream_listener 类型

参数 类型 说明
common wx_struct 基类
data 函数指针 系统调用此函数向微信小程序 SDK 提供摄像头采集到的视频数据。
error 函数指针 系统通过调用此函数告知微信小程序 SDK 摄像头采集视频数据出错。

注意事项:

  1. 微信小程序 SDK 目前仅采集 H264/H265 格式的视频数据。
  2. data 回调可以理解为非阻塞的,即与网络无关。
  3. 设备需要定时(建议 1 秒,最长不得超过 5 秒)提供 PPS、SPS、I 帧。
  4. 设备传入的 rotation 仅在 Linux SDK 0xD5000084 及以上版本、微信客户端 8.0.41 及以上版本生效,对于更旧的微信版本,需要在微信小程序侧调用 VoIP 插件的 setUIConfig 设置旋转角度。

5. (可选)视频模块

头文件:wmpf/hardware/video.h

摄像头模块,需要配置一个 wx_video_module 类型的实例。

运行时,微信小程序 SDK 将调用 create_output_stream 函数创建视频输出流,将接收到的远端(手机)摄像头录制的视频通过流对象输出。

5.1 create_output_stream

创建一个视频输出流。

wx_error_t (*create_output_stream)(struct wx_video_module* module,
                                   const struct wx_video_stream_config* config,
                                   struct wx_video_stream** stream);

参数

属性 类型 说明
module struct wx_video_module* context
config const struct wx_video_stream_config* 视频输出流参数
stream struct wx_video_stream** 返回视频输出流对象

返回值

wx_error_t 微信小程序 SDK 根据此返回值,判断视频输出流是否有效

错误码 说明
WXERROR_OK 成功

5.2 wx_video_stream

参数 类型 说明
common wx_struct 基类
write 函数指针 微信小程序 SDK 调用此函数以写入视频输出流。
close 函数指针 微信小程序 SDK 调用此函数以关闭视频输出流。