# Cloud-to-cloud services
# 1. Requirements for the operating environment
- The x86_64 runtime environment
- Glibc 2.28 or above
- Libcurl 7.84.0 or above
# 2. Service message receiving and pushing
After a call to the specified openid is initiated, if the call is successful, the WeChat open platform pushes the message to the developer background in accordance with the standard message push and method. Developers follow the Message Push guidelines to configure the callback address. Message encryption must select "Security Mode."
Note that the Vendor role may need to serve multiple Weixin Mini Program and can be developed using Third Party Platform.
The message pushed is like this:
{"ToUserName":"gh_27632a25xxx","Encrypt":"xxxx"}
// Encrypt decrypted content, refer to the field explaination.
After the developer background receives the message notification, it is necessary to reply to the request in time, otherwise it may trigger the retransmission of WeChat background, the correct reply format is as follows:
{"errcode":0,"errmsg":"ok"}
An errcode of 0 represents a successful reply, and the WeChat where the openid is located rings.If the errcode is not 0, the WeChat side will not ring, and the call will be cancelled.
Field Dxplaination:
| parameter | Data type | Introductions |
|---|---|---|
| Action | String | Platform access developer background event type, this interface fixed as join_voip_room |
| RoomId | String | Room id for this call |
| SessionKey | String | The sessionkey for this call |
| ServerToken | String | The server credentials for this call |
| Payload | String | A custom payload passed in by a third party when a call is initiated |
| ModelId | String | Modelid |
| Sn | String | Sn |
Example content:
{
"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. Service VOIP SDK
Server SDK is singleton mode , that is, the SDK can only handle one simultaneous audio and video call.Therefore, when the cloud server backstage connects to the server sdk, it is necessary to create a new process every time there is a new request, and call the server sdk interface within the process to complete the audio and video call.
Once you get the server_token and payload of the call through a message push, you can use the SDK to establish the call.
The server SDK requires a data file path, and calls wx_init to initialize it (no Weixin Mini Program AppID.Then, the VoIP room is added into the VoIP room through a join interface, and at the same time, the audio and video data flow of the device is received, the data is filled in or received through the relevant interface of the server-side SDK, and the communication of the audio and video flow between the device and the WeChat user is completed.
# 4. Interface Dxplaination
The specific interface is as follows, please refer to the demo code in the example directory of the sdk package.
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, // 小程序正式版
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,
* 需要先提交设备给微信审核通过后才能获得相关权限. 因此在接入调试期间,
* 你需要使用小程序开发版/体验版测试 VoIP 功能的可用性.
*
* 要设置为开发版还是体验版, 请向你的小程序开发同事咨询.
*
* 注意, 如果设备开发同事需要使用小程序开发版测试 VoIP 功能,
* 设备开发同事必须先获得小程序开发者权限,
* 并及时扫描小程序开发同事的微信开发者工具的真机调试二维码,
* 提前在本地微信客户端打开一次开发版小程序, 否则微信客户端在收到 VoIP
* 通话强提醒时, 会缺少小程序开发版代码包, 导致小程序启动失败.
*/
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