Tange Cloud for Device
 Rev.355
载入中...
搜索中...
未找到
双向呼叫开发指南

变更说明

旧(rev.345以前) 新(>=rev.345) 变动内容
TciAcceptInCall(INCALLINFO * )
TciRejectInCall(INCALLINFO *)
TciAcceptInCall2(ECALLFLAVOR flavor)
TciRejectInCall2()
参数变化
TgVdpAccept(INCALLINFO *)
TgVdpReject(INCALLINFO *)
TgVdpAccept2()
TgVdpReject2()
去掉了参数
STATUS_INCALL STATUS_INCALL2 回调参数不再由用户释放
TgVdpCallEx() TgVdpCallEx() 增加是否在接听前传输图像的参数(>=rev348)

1. 开发接口

我们提供了不同层次的开发接口,以满足不同用户和产品的需求。

功能 库及头文件 说明
Core libtgCloud.a(或 libtgCloud-tgwebrtc.a)
TgCloudApi.h
- 被叫。其它库都会依赖此库
- 可被动接收命令和发送应答(目前不能主动发送命令)
- 可发送消息到对端(无应答)
- API接口前缀: Tci
微信VoIP libtgWxVoip.a, libwxcloudvoip_client.a
wx.h
- 微信VoIP, 用于支持小程序。可以呼入和呼出
- 与Core库合用
dev-to-dev
主叫
libtgCallerP2p.a
icam_p2p.h
- dev-to-dev主叫库。 可以呼叫和连接另一台设备
- 可以发送命令并等待应答(同步)
- 接收被叫发来的消息
- API接口前缀: iCamP2p
双向呼叫 libtgVdp.a
vdp.h
- 主叫+被叫
- 调用了以上三个库-
- 封装了呼叫逻辑,支持同时呼叫到微信小程序和多台设备。
- 统一了主叫和被叫的音视频发送方法
- 开发者仍需调用少量被叫 TciXxxx 接口。
- WxVoIP 的接口被隐藏无需使用。

如上图,蓝线为库的依赖关系,绿框和橙框为两种库的选择组合:绿色组合的【应用1】模式,橙色组合的【应用2】模式

说明: 从 sdk rev.216起,libtgWxVoip.a作为libtgVdp.a下的一个可选插件存在。不需要微信呼叫功能时,不用链接 libtgWxVoip.a,从而减少目标程序空间及依赖。如果要支持微信呼叫,使用者要调用 wxInit() (在wx.h里申明)向sdk注册。

  • A1: 如果你已有IPC设备,要支持呼叫微信小程序,选择【应用2】,这种情况需要直接调用 wx.h 中接口(参见 2.2 WX VoIP 接口)。
  • A2: IPC设备,要支持呼叫另一个设备(即使不准备支持微信呼叫),选择【应用1】
  • A3: 开发楼宇对讲类似的应用,选择【应用1】。

【应用1】仍然需要用到少量【IPC开发包】的接口和数组结构。

2. 呼叫

2.1 Video Door Phone(VDP)接口

[Update at: 2026/06/20]

2.1.1 设备要求

  • 如果要支持微信呼叫,支持h264解码(微信小程序发往设备的视频只支持 H.264)
  • 配置设备的 Screen 能力

2.1.2 库和头文件

  • 包含 vdp.h / TgCloudApi.h
  • 如果要支持微信呼叫,包含 wx.h
  • 连接文首提及的所有的库(如果不用支持微信呼叫,则不需libtgWxVoip.a 和 libwxcloudvoip_client.a)

2.1.3 程序简介

最基本的VDP应用实现包括:

  • 一个 TciCB 结构,包含一组回调函数指针。结构中的回调有的必须实现(例如设置时间/时区,获取设备信息, 请求I帧等。demo里面有实现代码参考),有的是可选的(例如配网/ota升级)。
  • 一个可选的p2p命令回调

设置 TciCB 结构:

static struct TciCB _tci_cb;
static void init_tcicb()
{
_tci_cb.get_info = get_info;
_tci_cb.get_feature = get_device_feature;
_tci_cb.on_talkback_start = on_talkback_start;
_tci_cb.talkback = talkback;
_tci_cb.on_talkback_stop = on_talkback_stop;
_tci_cb.set_timezone = set_timezone;
_tci_cb.set_time = set_time;
_tci_cb.on_status = on_status;
_tci_cb.request_iframe_ex = request_iframe_ex;
};
void(* on_talkback_stop)(void)
结束对讲
int(* set_time)(time_t time)
设置时间
int(* request_iframe_ex)(int channel, int vstream)
请求指定图像通道的I帧
int(* on_talkback_start)(void)
开始对讲
int(* talkback)(TCMEDIA at, const uint8_t *audio, int len)
对讲数据回调 格式在前面已经协商过
int(* on_status)(int event, const void *pData, int len)
sdk内部状态
int(* set_timezone)(const char *tzs)
设置时区
int(* get_info)(TCIDEVICEINFO *info)
获取设备基本信息
sdk 回调函数结构

这里面值得一提的两个回调是是 TciCB::get_feature()

用户需要在 get_feature() 回调里:

  1. Screen 里返回屏幕配置。微信小程序只支持 640x480 / 480x640 / 320x240 / 240x320 四种尺寸的屏幕配置,并且发给设备的视频只支持h264编码
  2. CallTime 里设置呼叫超时值
/* 设备能力级. 完整描述参见文档 */
int get_device_feature(const char* key, char* buf, int bytes)
{
if(strcmp(key, "Screen") == 0) { //有屏的设备要在此返回屏幕配置。其它能力不是必需
strcpy(buf, "h264:320x240:0");
return 0;
}
else if(strcmp(key, "CallTime")) == 0 {
strcpy(buf, "30");
}
else
return -1; //其它不需要的能力查询返回 -1
return 0;
}

在 on_status() 回调里要处理呼入STATUS_INCALL(对室内机而言) 和 设备绑定状态(STATUS_LOGON/STATUS_DELETED)的处理, 实现细节见 vdp_demo.c:

int on_status(int status, const void *pData, int len)
{
switch(status)
{
//处理绑定事件
break;
//处理解绑
break;
case STATUS_UPDATE_SERVICE: //可以知道是不是有wx呼叫服务
break;
/* 作为被叫 */
{
INCALLINFO *pIci = (INCALLINFO*)pData;
//有呼入
}
}
return 0;
}
@ STATUS_DELETED
设备被删除. data: NULL.
@ STATUS_UPDATE_SERVICE
更新云服务. data: TCISERVICEINFO *; len: sizeof(TCISERVICEINFO)
@ STATUS_INCALL2
呼入状态通知.
@ STATUS_LOGON
设备上线. data: NULL.
呼叫者信息.

如果要支持微信呼叫,需要显示调用 wxInit() 注册微信voip模块(除此之外不需要调用任何其它 WX VoIP接口)。

程序的入口就很简单:

int main()
{
//初始化
init_tcicb();
TgVdpInit(cfg_path, &tci_cb);
TgVdpSetCmdHandler(p2p_cmd_handler); //可选
//如果要支持微信呼叫
wxInit();
//在此之前准备网络...
//启动服务
//其它业务
...
}
void TgVdpSetCmdHandler(TGCMDHANDLER func)
设置p2p命令处理回调, 代替 TciSetCmdHandler().
int TgVdpInit(const char *cfg_path, struct TciCB *cbs)
VDP 初始化.
int TgVdpStart(int isBound, const char *uuid)
开启VDP 服务.
int wxInit(void)
注册微信Voip模块.

在另外的线程里,向SDK送流。

while(1) {
TciSendFrameEx(0, 0, TCMEDIA_VIDEO_H264, ...); //主码流
TciSendFrameEx(0, 1, TCMEDIA_VIDEO_H264, ...); //辅码流
TciSendFrameEx(0, 0, TCMEDIA_AUDIO_G711A, ...); //音频
},
@ TCMEDIA_VIDEO_H264
H.264 "h264"
@ TCMEDIA_AUDIO_G711A
G.711A "g711a" "alaw"
int TciSendFrameEx(int channel, int stream, TCMEDIA mt, const uint8_t *pFrame, int length, uint32_t ts, int uFrameFlags)
发送实时音视频帧, SDK内部会将数据分发到云端和APP.

设备必须要支持双码流(主码流为高清,辅码流为VGA)。送给小程序的固定为辅码流,过高的码流容易导致小程序断开。

NOTE

TgVdpInit() 会定制自己的回调, 应用不能再调用TciInit()/TciSetCmdHandler()/TciSetCallback() 接口。如果要处理命令,调用 TgVdpSetCmdHandler() 来注册命令处理回调。

2.1.4 呼叫

sequenceDiagram
title: VDP 呼叫流程
participant A as 主叫A
participant B as 被叫B/C/...
participant S as 开发者后台
participant T as 探鸽平台
A->>S: 1.请求呼叫B/C/...
S->>T: 2.请求呼叫对象B/C/...的连接token
T-->>S: 3.返回呼叫对象的连接token
S-->>A: 4.连接token
A->>B: 5.TgVdpCallEx()
B->>B: 6.TgVdpAccept2()/TgVdpReject2()
B->>A: 7.应答(接听或拒接)
A->>A: 8.自动开始音视频收发

呼叫前先从平台获取呼叫对象列表(信息包含对象类型和连接token)。然后调用 TgVdpCallEx() 呼叫,TgVdpCallEx() 接收呼叫对象 struct CalleeEx 数组和起时值作参数,也就是可以一次呼叫多个用户。

TgVdpCallEx() 是异步的,成功调用仅表示发起了呼叫,呼叫/通话状态通过调用时提供的回调函数传出来。

//vdp.h:
int TgVdpCallEx(struct CalleeEx *callees, int size, int timeout, TGVDPCALLBACK cb);
//vdp_demo.c:
int vdp_callback(int status, void *pUser)
{
switch(status)
{
case TVCS_CALLING: printf("*** Calling ...\n"); break;
case TVCS_REJECTED: printf("*** Rejected.\n"); break;
case TVCS_ACCEPTED: printf("*** Call is accepted, in coversation ...\n"); break;
case TVCS_TIMEDOUT: printf("*** Call is timedout\n");
case TVCS_REMOTE_HANGUP: printf("*** Remote hangup.\n"); break;
}
return 0;
}
if(TgVdpCallEx(callees, n_calle, 20, vdp_callback) != 0)
return;
int(* TGVDPCALLBACK)(int status, struct CalleeEx *pCallee)
呼叫/通话状态回调.
定义 vdp.h:109
int TgVdpCallEx(struct CalleeEx *callees, int size, int timeout, TGVDPCALLBACK cb, const char *whoami, int bShowMeBeforeAccepted)
呼叫.
@ TVCS_TIMEDOUT
呼叫超时
定义 vdp.h:98
@ TVCS_ACCEPTED
接听
定义 vdp.h:97
@ TVCS_CALLING
呼叫中
定义 vdp.h:95
@ TVCS_REMOTE_HANGUP
对端挂断
定义 vdp.h:99
@ TVCS_REJECTED
拒接
定义 vdp.h:96
被叫
定义 vdp.h:55

2.1.5 取消呼叫/通话中挂断(主叫或被叫方)

这些功能都调用 TgVdpHangup(). 被叫挂断实际调用了 TciHangup().

TgVdpHangup() 是一个多功能接口。必须在 TgVdpCallEx() 成功后的任意时间调用一次。但不能在TgVdpCallEx() 的回调里调用。

2.1.6 数据收发

从Rev.349起,使用 TgVdpSendReq() / TgVdpSendResp() / TgVdpSendRespStatus() 发送请求或应答.

TgVdpSetCmdHandler() 注册的命令回调接收请求和应答

2.1.7 状态回调总结

应用通过回调获取通话过程的状态,而主叫和被叫的回调是不一样的。下表对此作了总结:

状态获取方式 状态
主叫 在TgVdpCallEx()中传入的回调 TVCS_CALLING, ///< 呼叫中
TVCS_REJECTED, ///< 拒接
TVCS_ACCEPTED, ///< 接听
TVCS_TIMEDOUT, ///< 呼叫超时
TVCS_REMOTE_HANGUP, ///< 对端挂断
TVCS_FAILED, ///< 呼叫失败
被叫 on_status(STATUS_INCALL2, pData, len) pData && len 呼入
pData && !len 取消呼入或超时(查看 INCALLINFO::state)
!pData && !len 对方挂断或接听超时

被叫的详细说明在第3章。

2.1.8 Demo

VDP的demo位于demo/Vdp 目录下:

vdp_demo.c 楼宇对讲框架演示,主叫和被叫都可以使用同一框架。

indoor_demo.c 楼宇对讲室内机框架,使用的是 IPC 库

2.2 WX VoIP 接口

[Update at: 2024/08/26]

2.2.1 设备要求

  • 支持h264解码(微信小程序发往设备的视频只支持 H.264)

2.2.2 库和头文件

  • 包含头文件 wx.h
  • 连接库 libtgWxVoip.alibwxcloudvoip_client.a

2.2.3 初始化

TciStart() 前调用 wxInit()

Profile 里添加描述: Profile={ "WxVoIP":1 }

如果有屏幕,设置 Screen. 格式只能是h264, 屏幕大小只支持 320x240/240x320/640x480/480x640 4种之一。例子: Screen=h264:240x320

2.2.4 呼叫和取消呼叫

调用 int wxCall(const char *openid) 呼叫小程序用户。

openid是小程序用户标识,由接口 wxGetUsers()返回(TIVS客户要使用别的接口)。

使用 wxCancelCall() 取消先前的呼叫。

2.2.5 状态通知

对微信呼叫来说,wxCall()和TciAcceptInCall2()是异步的,返回成功表示已呼出和开始接听。小程序端接听和挂断通过 p2p命令 TCI_CMD_ANSWERTOCALL 通知应用。

命令参数 Tcis_AnswerToCall::state 的取值有微调:其低16位为原来的呼叫状态 ECALLSTATE. 当状态为 CALLSTATE_ANSWERED 时,高16为应答来源(1表示应答来自微信小程序)

状态获取方式 状态
主叫 通过 TCI_CMD_ANSWERTOCALL 命令得到状态通知 CALLSTATE_MISSED, ///< 未接.
CALLSTATE_ANSWERED, ///< 接听
CALLSTATE_REJECTED, ///< 拒接
CALLSTATE_HANGUP, ///< 挂断或连接断。
CALLSTATE_BUSY ///< 用户占线
被叫 on_status(STATUS_INCALL2, pData, len) pData && len 呼入
pData && !len 取消呼入或超时(查看 INCALLINFO::state)
!pData && !len 对方挂断或接听超时

2.3 探鸽Caller接口

使用 VDP ,而不是直接调用此处提到的接口

2.3.1. 库和头文件

库文件: libtgCallerP2p.a

头文件: ConnectByToken.h, icam_p2p.h

2.3.2 呼叫流程

sequenceDiagram
title: Tange Caller 呼叫流程
participant A as 主叫A
participant B as 被叫B
participant S as 开发者后台
participant T as 探鸽平台
A->>S: 1.请求呼叫B
S->>T: 2.请求B的连接token
T-->>S: 3.返回B的连接token
S-->>A: 4.连接token
A->>B: 5.ConnectByToken(token)并且发送呼叫TCI_CMD_CALL
B->>B: 6.收到STATUS_CALL, 等待用户接听(或拒接)
B->>A: 7.应答TCI_CMD_CALL(接听或拒接)

3. 被叫(公共)

这部分逻辑是通用的,不受限于呼入类型。 这部分接口位于libtgCloud.a/TgCloudApi.h里。

3.1 呼入通知

用户呼叫设备时,设备端(被叫端)通过 TciCB::on_status(STATUS_INCALL2, pIci, ...) 回调收到 STATUS_INCALL2 通知。第二个参数pIci是一个INCALLINFO 指针, 其中有呼叫类型等信息。

如果 pIci 为 NULL, 则意味着对方取消呼叫。

  • 从 rev345 起,pIci 由SDK管理,应用不再负责释放

3.2 接听或拒接

应用不能阻塞on_status调用。在上面的回调里返回 0或负数SDK会自动拒接。但常规的应用是返回1,然后显示一个界面等待用户选择接听还是拒接。

  • 调用 TciAcceptInCall2()接听. 该函数可以传入一个参数来选择视频通话还是语音通话。接听有可能由于网络原因而失败,应用要检查返回值。
  • 调用 TciRejectInCall2() 拒接。

‍如果使用VDP接口,调用 TvdpAccept2()TvdpReject2()

不能在回调里调用这两个接口

3.3 通话中挂断

如果是主动挂断,调用:

如果是远端挂断,应用在命令处理回调(使用VDP接口时,通过TgVdpSetCmdHandler()注册)里通过命令 TCI_CMD_ANSWERTOCALL 收到通话状态(CALLSTATE_HANGUP).

3.4 接听超时

如果没用用户接听,在命令处理回调中通过 TCI_CMD_ANSWERTOCALL 还会收到超时通知(CALLSTATE_MISSED)。

因为主叫端同时存在超时计时,同时在呼通前不知道对方的超时设置,所以两端的超时应保持一致并且要事件配置好。

‍超时值通过能力 CallTime 配置。默认为30"

主叫和被叫谁先到达超时是不确定的。一端超时,会向另一端发出hangup通知,而另一端可能会在超时前收到此通知而提前结束。对开发者来说,超时也可能会收到 CALLSTATE_HANGUP. 这一点对主叫和被叫都适用。

参看 wxvoip_demo.c 或 vdp_demo.c 里的示例和说明。

4. 音视频之外的数据通信

前面的说明仅涉及 呼叫/接听/通话。如果开发者还需要传递音视频之外的数据,则需要进一步了解被叫和主叫使用的通信方式和开发接口。

4.1 可传递数据类型

主/被叫端媒体数据之外的数据(以下简称)有两种类型: 命令和消息。

  • 命令: 由主叫先发起。主给被叫发送请求,被叫被动响应并发送应答。
  • 消息:由被叫发出。单向

4.2 实现

主叫

  • 调用 iCamP2pExecute() (或其它预封装命令)发送命令和接收应答
  • 调用 iCamP2pSetCallback() 注册回调,接收从被叫端来的消息

被叫