常见问题(FAQ)

通话相关异常,请参考《通话异常排查指南》

1. 功能相关(通用)

1.1 如何限制用户的单次通话时长?

建议使用 initByCallertimeLimit 参数。插件低版本也可以根据 calling 事件的 keepTime 字段计算通话时长。超过限制后可以调用插件 forceHangUpVoip 中断通话。

不建议使用定时器实现此功能,容易出现一些异常情况导致定时器没有被清理的情况。导致影响后续通话。

1.2 在门禁、门锁场景,如何在手机端通话页面实现「开门」等功能?

插件提供了 setCustomBtnText 接口在手机端接听页面自定义按钮,开发者可配置一个自定义的弹层来实现具体功能。

C 端用户体验如下图所示:

1.3 用户如何取消授权?

用户可以在微信小程序设置页里取消授权,或通过在最近使用中删除微信小程序来清空授权记录。请参考「处理授权失效的情况」。

1.4 如何查询用户是否已授权设备(组)?

请参考「授权状态查询」。

1.5 如何设置呼叫超时时长(长时间不接听时停止呼叫)?是否支持轮询呼叫?

开发者可以自行控制超时时间,超时后调用插件 forceHangUpVoip 接口中断通话。

超时后,开发者可以根据业务场景,选择自动拨打给其他用户,实现轮询呼叫的能力。例如 101 号房有 A、B、C 三位业主,打给 A 业主 30 秒未接听,可自动打给 B 业主,依此类推。

1.6 如何自定义手机端看到的设备端来电名称?

为强化设备通话的认知、保证用户体验统一,手机端用户授权设备名称、接听设备来电的名称需保持一致。

授权设备名称 = 来电方名称 = 开发者自定义名称 + 设备类型名称。如「艾玛的希沃网课学习机」。开发者需要考虑名称显示,对名称做好规范。

(案例示意:订阅设备名称、来电方名称、语音通话中设备名称)

1.7 使用物联网卡时,如何配置域名和 IP 白名单?

VoIP 业务依赖于微信基础服务和微信小程序相关的业务内容,涉及比较多的 IP(在几千的量级)和域名,暂时未能提供完整的域名和 IP 列表,目前建议使用非定向的流量。

如有定向流量的需求,可在微信开放社区「硬件服务」板块发帖联系我们。

注意:IP 本身会随着业务的变更而增添或者裁撤,因而暂时没办法提供稳定的列表。

1.8 音视频通话的流量使用情况?

根据测算,语音通话大概是 2MB/分钟,视频是 10-30MB/分钟。

1.9 设备无摄像头或因隐私等原因不希望传画面(门禁、门锁的用户端),如何默认禁用摄像头?

插件发起通话时可以设置 caller.cameraStatus 或 listener.cameraStatus,设置两端是否默认开启摄像头,参见 initByCaller 接口文档。

如果要禁止用户切换摄像头,可以用插件的 setUIConfig 设置 callerUI/listenerUI 的 enableToggleCamera 选项。

1.10 为何推送消息显示的通话时长和通话结束或者 endVoip 事件获取的不一致?应该如何获取准确的通话时长?

VOIP 插件 2.2.1 及以下版本,通话结束页显示的时间为本地定时器计算的时间,endVoip 事件的 keepTime 提供的也是这个时间。但是由于通话双方之间存在一定网络延迟,这里的时间可能与实际扣费时长并不一致(一般要多于实际扣费的时长)。

VOIP 插件 2.2.2 版本开始,会在通话结束后(即 endVoip 事件后)从后台获取实际扣费时长,并通过 finishVoip 事件的 keepTime 返回给开发者。通话结束页也会更新显示实际的扣费时长。

2. 功能相关(安卓设备)

2.1 安卓应用和微信小程序之间如何进行参数传递和通信?安卓应用如何接收微信小程序发来的消息?
  1. 简单的「安卓应用 -> 微信小程序」单向单次传递参数的场景,可以直接在启动微信小程序的 path 中拼接 query。
  2. 如果安卓应用要接收微信小程序发来的事件、需要双向通信或者数据量大时可以通过 WMPF 提供的通信通道(Invoke Channel)
2.2 通话完成后如何关闭微信小程序?

当设备端微信小程序只承载 VOIP 通话能力时,可能需要在通话结束后将微信小程序切后台或关闭。

微信小程序收到插件的 endVoIP 事件后,通过 WMPF 提供的通信通道(Invoke Channel)通知 App。

收到通知后,App 可以选择调用 closeWxaApp 将微信小程序切后台或关闭(可参考《性能与体验优化指南》的说明选择)。

2.3 如何判断当前微信小程序是在设备端(WMPF)还是手机端打开

在 WMPF 运行时,微信小程序能可以访问到 wmpf 这个全局变量。可以通过是否存在这个全局变量来判断:typeof wmpf !== 'undefined' 即为设备端。

注意:调用 wmpf 上的方法前,应提前判断 wmpf 这个全局变量是否存在,否则在手机微信端走到这段逻辑时会报错。

3. 异常相关(通用)

3.1 手机端未收到微信通话强提醒或提醒强度不符合预期(锁屏未提醒、未响铃、未震动等)

请参考《通话提醒异常排查指南》。

3.2 获取设备票据 getSnTicket 接口返回 48001 (api unauthorized)

微信小程序 appId 未完成硬件设备接入导致。请确认:

  • appId 对应微信小程序已在「微信小程序管理后台」完成硬件设备接入。
  • 请求时使用的 access_token 是通过完成申请的微信小程序的 appId 申请的,而不是其他微信小程序的 appId 或者移动应用的 hostAppId。
3.3 wx.requestDeviceVoIP 报错 invalid scope

微信小程序 appId 未完成硬件设备接入或接入后未申请「微信小程序音视频能力」设备能力导致。请确认微信小程序已在「微信小程序管理后台」完成硬件设备接入并申请通过微信小程序音视频能力。

3.4 wx.getEnterOptionsSync 或插件的 getPluginEnteroptions 无法获取到进入微信小程序的 query

一般有以下几种情况:

  • 这两个函数只能获得微信小程序启动时(冷启动或热启动)的参数,如果是通过 wx.navigateTo 等路由方式跳转页面的情况,则需要在对应页面的 onLoad 生命周期获取。
  • 由于插件和宿主微信小程序的安全策略限制,当微信小程序启动路径为插件页面时,需要通过 VOIP 插件提供的 getPluginEnteroptions 获取 query;当微信小程序启动路径为微信小程序页面时,需要通过 wx.getEnterOptionsSync 获取 query。

建议排查时同时打印返回值中的 path 字段,确认是否是预期的传入 querypath

3.5 接听方接听时提示「页面不存在」

一般有以下几种情况:

  • 调用插件 initByCaller 时未设置 miniprogramState,或设置了 miniprogramState: formal,此时接听方会打开正式版微信小程序。而设备 VOIP 能力尚未发布正式版。
  • 调用插件 initByCaller 时设置了 miniprogramState: trial,此时接听方会打开体验版微信小程序。而当前设置为体验版的微信小程序中并未支持设备 VOIP 能力。
  • 调用插件 initByCaller 时设置了 miniprogramState: developer,此时接听方会打开开发版微信小程序。此时接听方需要提前扫码下载与拨打方相同的开发版微信小程序方可使用。
3.6 发起通话后,插件页面一直停留在「等待进行通话」界面无反应

一般有以下几种情况:

  • 微信小程序未调用插件 initByCaller 发起通话。可能是前置逻辑异常或未走到发起通话的分支。开发者应首先确定调用了该接口。
  • 微信小程序调用插件 initByCaller 失败,可能会抛出异常或者返回了非 0 的 errCode。开发者应正确地捕获和处理接口异常,并给用户必要的提示。
3.7 为什么我在 wecopper 的设备管理里找不到公钥?

这是因为你的设备类型是微信支付刷脸设备,目前这类设备不支持硬件 Voip 模式,需要重新申请设备类型。

4. 异常相关(安卓设备)

4.1 WMPF 获取不到正确的摄像头,或摄像头画面旋转

可以使用 InitGlobalConfig 接口指定微信小程序使用的摄像头,也可以指定摄像头画面的旋转角度。

fun initGlobalConfig() {
    val jsonConfig = JSONObject()
        // 请注意:USB 摄像头和内置摄像头使用的参数名称是不一样的。
    try {
        // 案例 1:微信端画面颠倒
        jsonConfig.put("cameraPushFlip", true) // USB 摄像头需使用 usbCameraPushFlip 参数

        // 案例 2:使用内置摄像头,微信端显示画面旋转
        jsonConfig.put("cameraRotationAngle", 90) // 根据实际情况调整角度

        // 案例 3:通过指定 internalCameraName 使用设备内置摄像头(需 WMPF 2.0.0 支持)
        jsonConfig.put("internalCameraName", "xxxx")

        // 案例 4:通过指定 cameraId 使用设备内置摄像头
        jsonConfig.put("cameraId", 0)

        // 案例 5:通过直接指定摄像头设备路径使用 USB 摄像头(与案例 5 的情况二选一)
        jsonConfig.put("usbCameraName", "/dev/xx/xx/xx")

        // 案例 6:通过指定三元组使用 USB 摄像头(与案例 4 的情况二选一)
        jsonConfig.put("usbCameraProductId", 0)
        jsonConfig.put("usbCameraVendorId", 0)
        jsonConfig.put("usbSerialNumber", "xxx")

        // 案例 7:使用 USB 摄像头,WMPF 预览和微信端显示画面旋转
        jsonConfig.put("usbCameraRotationAngle", 90) // 根据实际情况调整角度

        val json = jsonConfig.toString()
        LogUtils.d(TAG, "initGlobalConfig", json)
        Api.initGlobalConfig(json)
            .subscribe({
                LogUtils.d(TAG, GsonUtils.toJson(it))
                warmLaunch()
            }, {
                LogUtils.d(TAG, GsonUtils.toJson(it))
                warmLaunch()
            })
    } catch (e: Exception) { }
}

如果设置摄像头画面旋转未生效,建议按照下列指引检查:

  • InitGlobalConfig 必须在 ActivateDevice 回调成功后、启动微信小程序前调用。建议在 ActivateDevice 的 onSuccess 回调后调用。
  • InitGlobalConfig 设置是一次性的,在每次 WMPF 启动后都需要调用。
  • 请确认设备使用的是 USB 摄像头还是内置摄像头
    • 如果通过 usbCameraName,或 usbCameraProductId + usbCameraVendorId + usbSerialNumber 指定使用 USB 摄像头,需使用 usbCameraPushFlipusbCameraRotationAngle 设置画面旋转
    • 其他情况下使用内置摄像头,可以使用 internalCameraName 指定摄像头 cameraId。此时需使用 cameraPushFlipcameraRotationAngle 设置画面旋转。此时只能设置微信客户端看到的推流画面的旋转,不能改变设备端看到的预览画面。