硬件抽象层

微信小程序 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 调用此函数以关闭视频输出流。

微信小程序用户授权设备

设备如果要向用户发起通话,需要用户在手机微信端先对设备进行授权。

微信呼叫设备,若使用 callWMPF 或 callDevice 接口不需要授权。使用 initByCaller 接口仍需要授权。

1. 请求授权

用户授权前,需要从开发者的后台通过获取设备票据 接口拿到设备票据 snTicket。

拿到 snTicket 后,需要在微信小程序内调用 wx.requestDeviceVoIP 请用户进行授权。

需要基础库 >= 2.27.3 支持。设备组 >= 2.30.4 支持。

wx.requestDeviceVoIP({
  sn: 'xxxxxx', // 向用户发起通话的设备 sn(需要与设备注册时一致)
  snTicket: 'xxxxxx', // 获取的 snTicket
  modelId: 'xxxxxx', // 「设备接入」从微信公众平台获取的 model_id
  deviceName: 'xxx', // 设备名称,用于授权时显示给用户
  success(res) {
    console.log(`requestDeviceVoIP success:`, res)
  },
  fail(err) {
    console.error(`requestDeviceVoIP fail:`, err)
  },
})

注意:

  • 如果用户拒绝授权或在设置页中取消授权,再次调用 requestDeviceVoIP 不会出现授权弹框。开发者应引导用户在设置页中手动开启。
  • 授权框中「设备名字」= 「deviceName」 + 「modelId 对应设备型号」。如「devcieName」为「iot」,modelId 对应设备型号是「校园电话」,最终名字为「iot 校园电话」

2. 处理授权失效的情况

用户在授权成功后,下列操作可能导致授权失效:

  • 清空授权:在最近使用中删除微信小程序,用户的授权记录会被清空
  • 取消授权:用户同意授权后,微信小程序设置页面中会出现「语音、视频通话提醒」模块,点击进入后用户可以管理已授权的设备,并可以取消授权。(需要微信客户端 >= 8.0.30 支持)

为了保证用户能够正常使用音视频通话能力,开发者需要处理授权失效的情况。 在发起通话前,建议开发者通过第 4 节所述方式检查授权状态。并在必要时提醒用户重新授权:

  • 清空授权:可以直接调用 requestDeviceVoIP 请用户进行重新授权。
  • 取消授权/用户拒绝授权:再次调用 requestDeviceVoIP 不会出现授权弹框。开发者应引导用户在设置页中手动开启授权开关。

开发者可以通过以下方式判断用户的授权状态:

  • 通过发起通话失败的错误码。使用插件发起通话时,若用户未授权设备,会返回 errCode: 9。(若使用设备组,请确认设备组内存在此设备)
  • 在用户使用微信小程序时,查询授权状态。参见第 4 节。

3. 批量授权

如果需要批量授权,可以创建设备组。在用户授权设备进行音视频通话时,可以批量授权给一个设备组,而无需对每台设备重复授权。

例如,在校园电话场景下,同一所学校可能有很多台话机。可以将同一所学校的设备加入到一个设备组,并使用 wx.requestDeviceVoIP 对整个设备组进行授权。

注意:对于设备组,deviceName 显示为创建设备组时指定的名称,授权时暂不允许自定义。

需要基础库 >= 2.30.4 支持。

wx.requestDeviceVoIP({
  isGroup: true,
  groupId: '设备组 ID',
  success(res) {
    console.log(res)
  },
  fail(res) {
    console.log(res)
  },
})

4. 授权状态查询

开发者可以在用户使用微信小程序时,通过下列方式查询授权状态。根据微信小程序统一的授权体系设计,不提供后台接口查询授权状态,也不提供用户操作授权的事件回调。

4.1 当前用户授权的设备(组)

查询当前登录的用户同意/拒绝或取消授权了哪些设备(组)。

手机微信端微信小程序内调用 wx.getDeviceVoIPList,可用于在手机端发起通话前检查授权状态。

需要基础库 >= 2.30.3 支持。设备组 >= 2.30.4 支持。

// 微信小程序基础库接口
wx.getDeviceVoIPList({
  success(res) {
    console.log('[getDeviceVoIPList]', res.list)
    // [{sn: 'xxx', model_id: 'xxx', status: 0}]
    // status: 0/未授权;1/已授权
  },
})
  • 设备组只有 groupId 字段,sn 和 model_id 为 undefined

4.2 当前设备是否被授权

根据用户 openId,查询指定用户是否授权设备(组)。

由插件提供 getIotBindContactList 接口,一般在设备端使用,可用于在设备端发起通话前(如联系人页面)检查授权状态。

const wmpfVoip = requirePlugin('wmpf-voip').default

wmpfVoip
  .getIotBindContactList({
    sn: '设备sn',
    model_id: '申请的modelid',
    openid_list: ['openid_1', 'openid_2'], // 传入需要验证的openid列表
  })
  .then(res => {
    console.log(`[getIotBindContactList]:`, res.contact_list)
    // [{sn: 'xxx', model_id: 'xxx', status: 0}]
    // status: 0/未授权;1/已授权
  })

设备呼叫手机微信

在用户对设备进行授权后,设备可以向已授权用户发起音视频通话,用户在微信内打开微信小程序进行接听。

硬件开发者需建立微信小程序用户 openId、微信小程序 appId、硬件设备之间的关联。用户在手机端授权后设备才可拨打。

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 设备端发起通话(安卓直连)

发起通话前,一般需要用户选择拨打给的用户和通话的类型(音频/视频)。

根据业务场景不同,发起通话前的流程(如选择联系人和房间类型)可以在微信小程序的另一个页面中或者安卓应用中进行。

1.1 微信小程序页面进入通话页面

适用于用户发起通话前的页面(如联系人选择等)是微信小程序页面时。

发起通话时,设备端需要在之前的页面中调用插件的 initByCaller 接口,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

try {
  // 2.4.0 以下版本 roomId 为 groupId
  const { roomId, isSuccess } = await wmpfVoip.initByCaller({
    caller: {
      id: 'sn', // 设备 SN
      // 不支持传 name,显示的是授权时「deviceName」+「modelId 对应设备型号」
    },
    listener: {
      // 参见 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 获取
      id: 'openId' // 接听方 用户 openId
      name: 'xxxxxx', // 接听方名字,仅显示用
    },
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    businessType: 1, // 1 为设备呼叫手机微信
    voipToken: 'xxxxxxxxxx', // 使用设备认证 SDK 注册的设备传入 deviceToken,使用 WMPF RegisterMiniProgramDevice 接口注册的设备无需传入(插件 2.3.0 支持)
    miniprogramState: 'formal', // 指定接听方使用的微信小程序版本
  })

  if (isSuccess) {
    // 如果微信小程序启动直接进入插件页面,则不要调用 wx.redirectTo
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
      // 插件 2.3.9 开始支持 CALL_PAGE_PATH, 低版本请传入 'plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin',
    })
  } else {
    wx.showToast({
      title: '呼叫失败',
      icon: 'error',
    })
  }
} catch (e) {
  // 参数错误的情况会通过异常抛出
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

1.2 安卓应用直接进入通话页面

适用于用户发起通话前的页面(如联系人选择等)是安卓应用页面时。

发起通话时,需要安卓应用调用 WMPF launchMiniProgram 接口拉起微信小程序,path 直接使用插件的拨打页面 plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin。路径后可以带自定义参数,如 &a=1

这种情况下,开发者可以直接在微信小程序 App.onShow 时调用 initByCaller,插件会直接进入拨打状态,不需要也不可以再跳转到插件页面

强烈建议开发者在启动参数中增加防重放参数,例如callSeq=1703741306977。参数名可以自定义,取值可以是时间戳或其他唯一 ID。主要起到以下功能

  • 如果用户点击重新进入微信小程序,避免重复发起通话。
  • 微信小程序切后台又切前台的情况(如进入微信小程序设置页、进入其他原生页面、用户手动操作等),App.onShowcallPageOnShow 会多次触发,避免重复发起通话。
  • 标识当前启动是会发起通话,方便一些逻辑判断。

建议开发者使用「微信小程序预热」能力加快微信小程序的启动速度。

const wmpfVoip = requirePlugin('wmpf-voip').default

// 假设预热启动参数为 ?isPreLaunch=1
// 假设发起呼叫时启动参数为 ?callSeq=1703741306977&..其他呼叫用的参数

function checkCallSeq(seq) {
  if (!seq) return false
  // 重新进入微信小程序会重启微信小程序,因此 seq 需要持久化存储,不能仅存变量
  const lastCallSeq = wx.getStorageSync('WMPF_CALL_SEQ')
  if (seq !== lastCallSeq) {
    // 示例仅做简单的比较,开发者可以根据业务需要增加其他判断条件,如 parstInt(seq) > parseInt(lastCallSeq)
    wx.setStorageSync('WMPF_CALL_SEQ', seq)
    return true
  } else {
    // 重复发起的通话
    return false
  }
}

function call(options) {
  try {
    wmpfVoip
      .initByCaller({
        /* 参数可从 options 中获取,此处省略 */
      })
      .catch(e => {
        wx.showToast({
          title: '呼叫失败',
          icon: 'error',
        })
      })
  } catch (e) {
    // 参数错误的情况会通过异常抛出
    wx.showToast({
      title: '呼叫失败',
      icon: 'error',
    })
  }
}

/**
 * 调用 WMPF LaunchMiniProgram 时,
 *  - 如果微信小程序不在前台(未启动或在后台),会触发 App.onShow
 *  - 如果微信小程序在前台,App.onShow 不会触发,但会触发插件的 callPageOnShow
 */

App({
  onShow() {
    const { query } = wmpfVoip.getPluginEnterOptions()
    if (query.isPreLaunch) {
      // 微信小程序预热场景,无需处理
      return
    }

    if (!query.callSeq) {
      // 当前启动不是发起通话,无需处理
      return
    }

    if(!checkCallSeq(query.callSeq)) {
      // 重复发起通话,直接忽略
      return
    }

    call(query)
  },
})

wmpfVoip.onVoipEvent(event => {
  if (event.eventName === 'callPageOnShow') {
    // 仅处理微信小程序在前台时,调用 WMPF LaunchMiniProgram 触发<a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a> reLaunch 的情况
    const query = wmpfVoip.getPluginOnloadOptions()
    if (checkCallSeq(query.callSeq)) { 
      // 此处可以过滤掉已被 App.onShow 处理的情况
      call(query)
    }
  }
})

注意

  • 给用户推送的接听提醒会在调用 initByCaller 后由微信后台直接下发,不需要开发者额外调用服务端下发消息的接口。
  • roomType 等参数可以通过拉起微信小程序的 path 中的 query 传递给微信小程序。
  • 设备上,APP 拉起微信小程序或接听通话较慢时,请参考性能与体验优化指南。

2. 设备端发起通话(Linux 直连)

请参考 《微信小程序音视频通话 SDK (Linux)》 5.4.1 发起通话部分

3. 手机微信端接听通话

用户在手机端可以收到「响铃+振动」的强提醒通知,点击接听按钮后,会启动微信小程序并直接进入「VOIP 通话」插件页面接听通话。

完成通话后,微信客户端内会显示本次通话的信息与「关闭」按钮,用户点击「关闭」按钮后再跳转开发者调用setVoipEndPagePath设置的页面。开发者未设置时则直接关闭微信小程序。

开发者可以自定义接听页面按钮,以及通话结束跳转页。详情请参考插件文档

4. 设备端处理通话结束(安卓直连)

设备端通话结束后,开发者需自行处理页面跳转或关闭微信小程序。一般有以下几种方式:

  • 结束后跳转其他页面:开发者需要通过插件setVoipEndPagePath接口设置通话结束跳转的页面。开发者未设置时则停留在通话记录页面。
  • 结束后微信小程序切后台:开发者可以监听插件 endVoip 或 finishVoip 事件,通过 WMPF 提供的通信通道(Invoke Channel)通知移动应用,使用closeWxaApp (keepRunning=true) 将微信小程序切后台。
  • 结束后关闭微信小程序:开发者可以监听插件 endVoip 或 finishVoip 事件,调用 wx.exitMiniProgram 关闭微信小程序

手机微信呼叫设备(安卓)

用户可以向设备发起音视频通话,设备端需要开发者在收到消息后拉起微信小程序的指定页面让用户接听通话。

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 手机微信端发起通话

发起通话前,一般需要用户在微信小程序中选择拨打的设备和通话的类型(音频/视频)。

发起通话时,开发者需要先从后台拿到从设备端获取的 pushToken,并在微信小程序中调用插件的 callWMPF 接口,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

const roomType = 'video'
try {
  const { roomId, isSuccess } = await wmpfVoip.callWMPF({
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    sn: '设备 SN',
    modelId: '设备 modelId',
    pushToken: '从设备获取的 pushToken',
    nickName: '设备端显示的微信用户名称',
    deviceName: '我的学习机',
    envVersion: 'release', // 指定接听方使用的微信小程序版本,开发过程可以使用 develop
  })

  if (/* 当前不在插件页面 */) {
    // 跳转到插件的通话页面
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
      // 插件 2.3.9 开始支持 CALL_PAGE_PATH, 低版本请传入 'plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin',
    })
  }
} catch (e) {
  console.error('callWMPF failed:', e)
  // 参数错误的情况会通过异常抛出
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

注意

  • 建议开发者在服务端维护 sn 与 pushToken 的关联,提前在设备端获取 pushToken并存到后台,并在 pushToken 过期前进行刷新。
  • 发起通话时,需要保证设备已激活并联网在线
  • 给设备推送的消息会在调用发起通话接口后由微信后台直接下发,不需要开发者额外调用服务端下发消息的接口。
  • 插件 2.4.0 以下版本,需使用initByCaller接口呼叫设备,传入 businessType: 2。使用 initByCaller 接口发起的通话,需要用户额外进行授权。

2. 设备端接听通话(安卓)

在手机微信端发起通话后,设备端会收到一条 WMPF 的推送消息。需要开发者处理通知并拉起微信小程序展示通话界面:

2.1 绑定消息监听

开发者需要在 WMPF 中调用 registerPushMsgEventListener 注册消息监听。注意,这一步必须在通话发起前进行。

2.2 展示来电通知/提醒(可选)

收到消息后,开发者可以根据产品需要展示来电通知(样式可以自定义),也可直接拉起微信小程序让用户进行接听。

2.3 打开微信小程序接听

推送消息为 JSON 字符串,解析后格式如下

{
  "path": "plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin?roomType=roomType&groupId=groupId&listenerId=设备sn&callerName=拨打方名称&customQuery字符串", // 微信小程序启动路径
  "appType": 0, // 0: 正式版 1: 开发版 2: 体验版
  "appid": "wx********" // <a href="https://weixin-xiaochengxu-kaifa.yuannext.com">微信小程序</a>appid
}

开发者需要使用上述参数,调用 WMPF launchMiniProgram 接口打开微信小程序的接听界面。

注意

  • 如果开发者在收到消息后展示了自定义的来电通知,可以在启动微信小程序的 path 后添加 &isClickedHangOnBtn=1。此时用户进入微信小程序就会直接接听通话,不需要再次点击插件通话页面「接听」按钮。
  • 建议在安卓 APP 中使用 WMPFLifeCycleManager 监听 WMPF 退出事件,并重新启动并激活 WMPF,以防止 WMPF 异常退出后消息丢失。
  • 为加快接听通话的速度,建议在收到消息监听时调用prefetchDeviceToken 预拉取设备凭证。
  • 设备上,APP 拉起微信小程序或接听通话较慢时,请参考性能与体验优化指南。

3. 设备端处理通话结束

设备端通话结束后,开发者需自行处理页面跳转或关闭微信小程序。一般有以下几种方式:

  • 结束后跳转其他页面:开发者需要通过插件 setVoipEndPagePath 接口设置通话结束跳转的页面。开发者未设置时则停留在通话记录页面。
  • 结束后微信小程序切后台:开发者可以监听插件 endVoip 或 finishVoip 事件,通过 WMPF 提供的通信通道(Invoke Channel)通知移动应用,使用closeWxaApp (keepRunning=true) 将微信小程序切后台。
  • 结束后关闭微信小程序:开发者可以监听插件 endVoip 或 finishVoip 事件,调用 wx.exitMiniProgram 关闭微信小程序。

手机微信呼叫设备(Linux 直连)

需插件 2.4.0 版本、Linux SDK 0x00097 开始支持

如果要获取通话过程的各类事件,可以使用插件的 onVoipEvent 接口。

1. 手机微信端发起通话

发起通话前,一般需要用户在微信小程序中选择拨打的设备和通话的类型(音频或视频)。

发起通话时,开发者需要在微信小程序中调用插件的 callDevice 接口获取 roomId,然后跳转到插件的发起通话页面。

const wmpfVoip = requirePlugin('wmpf-voip').default

try {
  const { roomId } = await wmpfVoip.callDevice({
    roomType: 'video', // 房间类型。voice: 音频房间;video: 视频房间
    sn: '设备 SN',
    modelId: '设备 modelId',
    nickName: '设备端显示的微信用户名称',
    deviceName: '我的学习机',
  })

  if (/* 当前不在插件页面 */) {
    wx.redirectTo({
      url: wmpfVoip.CALL_PAGE_PATH,
    })
  }
} catch (e) {
  console.error('callDevice failed:', e)
  wx.showToast({
    title: '呼叫失败',
    icon: 'error',
  })
}

2. 推送通话提醒

手机微信内发起通话后,开发者应使用自有消息通道,将 roomId 等设备端加入房间所需参数传递给设备。

3. 设备端接听通话

设备端在收到通话提醒后,首先使用 wx_voip_session_new 接口创建 Session,然后通过 wx_voip_listener_join 接听。其他接口使用与设备呼叫手机类似。Session 创建后,可以调用 wx_voip_session_hangup 接口结束通话。详情可参考《微信小程序音视频通话 SDK (Linux)》。

4. 设备端拒绝通话

设备端收到通话提醒后,可以调用 wx_voip_listener_hangup(只能在 Session 创建前调用)结束通话,实现忙线或拒接。详情可参考《微信小程序音视频通话 SDK (Linux)》。

性能与体验优化

要让用户的接听和拨打体验更加流畅,关键是要缩短接听和拨打时微信小程序启动和一些网络请求的耗时。

微信小程序的冷启动需要一定的时间,尤其是在性能较差的设备上,启动耗时可能会偏长。影响用户拨打和接听音视频通话的体验。

1. 微信小程序侧启动性能优化

建议开发者参考《启动性能优化文档》优化微信小程序的启动耗时。

2. 安卓 WMPF 微信小程序预热(建议)

在设备端 WMPF,我们额外提供了「微信小程序预热」的能力,在用户使用微信小程序前,就预先将微信小程序在后台以无界面的形式启动并常驻运行,以便用户使用时可以直接把微信小程序切前台,而不需要完整进行冷启动流程。流程如下:

  • WMPF 激活后,在用户使用微信小程序之前,可以调用warmUpApp提前预热微信小程序。
    • 通常情况下,建议指定 path 为插件的拨打/接听页面 plugin-private://wxf830863afde621eb/pages/call-page-plugin/call-page-plugin?isPreLaunch=1。如果开发者需要微信小程序启动时打开其他页面(例如联系人列表页),也可以指定预热其他页面。
  • 设备端发起或接听通话,真正需要使用微信小程序时,再调用launchMiniProgram传入正常的带有 query 的 path 等启动参数,即可复用之前预热的环境,把微信小程序拉到前台。
    • 预热和正式使用时传入的 path 参数的路径部分需保持一致,query 部分可不同。否则会额外触发一次页面的 reLaunch