帮你快速理解、总结文档立即下载

Linux & RTOS 设备

最近更新时间:2026-09-20 16:56:01
本文档已由 AI 辅助审校
我的收藏
TWeCall 是腾讯云物联网开发平台提供的设备与微信小程序之间的双向音视频通话能力。设备端一键呼叫,微信小程序端持续响铃提醒,接通后即可进行音视频通话,达到微信原生通话体验。
本文将介绍设备如何实现获取小程序联系人列表呼叫微信小程序响应通话状态回调挂断通话,帮助您快速完成 TWeCall(微信通话)的设备端接入。

前提条件

已完成 登录与注册 文档中的登录功能,设备能够成功 tc_iot_login 并保持在线。
已在物联网开发平台完成 TWeCall 服务激活与小程序授权,详见 微信通话服务(TWeCall)
已完成小程序接入文档 设备呼叫小程序 中授权小程序音视频通话。

接入步骤

TWeCall 设备端接入分为以下步骤:
1. 注册通话事件回调。
2. 获取联系人列表。
3. 发起呼叫。
4. 处理通话状态回调。
5. 创建通话推流通道并推送音视频帧。
6. 挂断通话。

步骤 1:注册通话事件回调

调用 tc_iot_av_init 初始化音视频模块并注册通话事件回调。设备作为主叫时,需要关注对端的接听、拒绝、超时、挂断回调;设备作为被叫时,需要关注来电请求回调。
tc_iot_error_e tc_iot_av_init(const tc_iot_av_observer_s *observer);
observer 回调说明
回调
触发时机
说明
on_call_requested
收到对端(小程序)发起的来电请求。
设备作为被叫时触发,在此回调中调用
tc_iot_av_accept 接听或 tc_iot_av_reject 拒绝。
on_call_accepted
对端接听呼叫。
设备作为主叫时触发,此时可开始推送音视频。
on_call_rejected
对端拒绝呼叫。
设备作为主叫时触发。
on_call_timeout
对端无人接听,呼叫超时。
设备作为主叫时触发。
on_call_hangup
对端主动挂断。
通话过程中对端挂断时触发。
on_audio_frame_received
收到对端音频帧。
用于播放对端音频。
on_video_frame_received
收到对端视频帧。
用于播放对端视频。
注意:
observertc_iot_av_init 时会被 SDK 内部拷贝,传入栈变量即可,无需长期保活。
// 通话状态回调
static void on_call_accepted(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_accepted: user_id=%s\\n", contact->user_id);
// 对端已接听,开始上推音视频(见步骤 5)
start_call_streams();
}

static void on_call_rejected(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_rejected: user_id=%s\\n", contact->user_id);
// 对端拒接,清理本地状态
}

static void on_call_timeout(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_timeout: user_id=%s\\n", contact->user_id);
// 呼叫超时,清理本地状态
}

static void on_call_hangup(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_hangup: user_id=%s\\n", contact->user_id);
// 对端挂断,停止推流并清理通道(见步骤 5/6)
stop_call_streams();
}

// 被叫:收到来电请求
static void on_call_requested(const tc_iot_contact_s *contact, tc_iot_call_option_t option) {
printf("[twecall] on_call_requested: user_id=%s media=%d quality=%d\\n",
contact->user_id, option.media_content, option.video_quality);
// 这里选择接听,也可调用 tc_iot_av_reject 拒绝
tc_iot_av_accept(contact, NULL);
}

// 初始化
tc_iot_av_observer_s observer;
memset(&observer, 0, sizeof(observer));
observer.on_call_requested = on_call_requested;
observer.on_call_accepted = on_call_accepted;
observer.on_call_rejected = on_call_rejected;
observer.on_call_timeout = on_call_timeout;
observer.on_call_hangup = on_call_hangup;

tc_iot_error_e rc = tc_iot_av_init(&observer);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_av_init failed: %d\\n", rc);
}

步骤 2:获取联系人列表

调用 tc_iot_av_get_contacts 拉取可呼叫的微信小程序联系人列表。接口采用游标(cursor)分页,首次调用传 NULL,后续传入上一次回调返回的 next_cursor 继续拉取,直到 next_cursorNULL 表示已到末尾。
说明:
如果设备调用 tc_iot_av_get_contacts 后获取到联系人列表为空,说明小程序没有正常完成音视频授权。建议用户将小程序删除,重新添加小程序。小程序第一次授权时,会有如下弹窗提示。

tc_iot_error_e tc_iot_av_get_contacts(const char *cursor, uint32_t limit,
tc_iot_av_contacts_cb callback);
参数
说明
cursor
分页游标,首次传 NULL(SDK 内部等价于 "0"),后续传入上次回调的 next_cursor
limit
本次拉取数量,有效范围 (0, 1024]。
callback
结果回调,原型见下。
回调原型
typedef void (*tc_iot_av_contacts_cb)(tc_iot_error_e error_code, const char *error_message,
const tc_iot_contact_s *contacts, uint32_t count,
const char *next_cursor);
参数
说明
error_code
TC_IOT_ERR_SUCCESS 表示成功,其余为失败码。
contacts
联系人数组指针,失败时为 NULL
count
本次返回的联系人数量,0 表示已无更多联系人。
next_cursor
下一页游标,NULL 表示已是最后一页,无需继续拉取。
联系人结构体
typedef struct {
const char *user_id; // 呼叫目标标识,格式 "modelId/wxAppId/openId"
const char *user_name; // 用户昵称(可能为空)
const char *avatar_url; // 头像 URL(可能为空)
} tc_iot_contact_s;
说明:
user_id 是发起呼叫的关键标识,由 modelIdwxAppIdopenId 三段以 / 拼接而成。该字符串内存由 SDK 在回调期间管理,回调返回后会被释放,如需长期使用请自行拷贝。
示例:完整拉取所有联系人
static const char *g_target_user_id = NULL;

static void on_get_contacts(tc_iot_error_e error_code, const char *error_message,
const tc_iot_contact_s *contacts, uint32_t count,
const char *next_cursor) {
printf("[twecall] get_contacts: code=%d count=%u next_cursor=%s\\n",
error_code, count, next_cursor ? next_cursor : "(end)");

if (error_code != TC_IOT_ERR_SUCCESS) {
printf("get_contacts failed: %s\\n", error_message ? error_message : "");
return;
}

for (uint32_t i = 0; i < count; i++) {
printf(" [%u] user_id=%s name=%s\\n", i, contacts[i].user_id,
contacts[i].user_name ? contacts[i].user_name : "");
// 记录第一个联系人作为呼叫目标
if (g_target_user_id == NULL) {
g_target_user_id = strdup(contacts[i].user_id);
}
}

// next_cursor 非空说明还有更多联系人,继续翻页
if (next_cursor != NULL) {
tc_iot_av_get_contacts(next_cursor, 50, on_get_contacts);
}
}

// 首次拉取,每页 50 条
tc_iot_av_get_contacts(NULL, 50, on_get_contacts);

步骤 3:发起呼叫

呼叫体验版小程序(重要)

特别强调:
开发调试阶段,设备端必须初始化之后、登录之前调用 tc_iot_call_experiment_api 显式指定呼叫的小程序版本为体验版(DEMO)或开发版(DEBUG),否则会因小程序版本不匹配导致呼叫失败或无法接通

/*
"DEBUG" 开发版
"DEMO" 体验版
*/
tc_iot_call_experiment_api("set_wx_miniapp_flavor", "DEMO", NULL);

待完成体验版功能自测后,将设备寄送至微信团队进行验证。验证通过后,小程序的 VoIP 音视频能力方可正式上线发布,小程序 VoIP 音视频正式上线后,无需再调用此接口,SDK 将默认呼叫线上版小程序。
选定联系人后,调用 tc_iot_av_call 向该联系人(微信小程序)发起呼叫。接口为异步调用,结果通过 callback 返回。
tc_iot_error_e tc_iot_av_call(const tc_iot_contact_s *contact, tc_iot_call_option_t option,
tc_iot_av_operation_cb callback);
参数
说明
contact
呼叫目标,contact.user_id 必填(取自 tc_iot_av_get_contacts 返回的 user_id)。
option
呼叫选项,见下表。
callback
操作结果回调:TC_IOT_ERR_SUCCESS 表示呼叫请求已成功发出,对端是否接听需等待 步骤4 的状态回调。
呼叫选项 tc_iot_call_option_t
字段
说明
media_content
媒体内容,见下表枚举。
video_quality
视频清晰度,见下表枚举。
custom_data
自定义数据(最长256字节),随呼叫信令透传到小程序端。
媒体内容枚举 tc_iot_media_content_e
取值
说明
TC_IOT_MEDIA_CONTENT_AUDIO
仅音频通话。
TC_IOT_MEDIA_CONTENT_VIDEO
仅视频通话。
TC_IOT_MEDIA_CONTENT_AUDIO_VIDEO
音视频通话(默认推荐)。
清晰度枚举 tc_iot_video_quality_e
取值
说明
TC_IOT_VIDEO_QUALITY_LD
流畅(低清晰度)。
TC_IOT_VIDEO_QUALITY_SD
标清。
TC_IOT_VIDEO_QUALITY_HD
高清。
TC_IOT_VIDEO_QUALITY_FHD
超清(全高清)。
示例
static void on_call_op(tc_iot_error_e error_code, const char *error_message) {
printf("[twecall] call op: code=%d msg=%s\\n", error_code, error_message ? error_message : "");
}

// 使用联系人列表中的 user_id 发起音视频呼叫
tc_iot_contact_s contact = {0};
contact.user_id = g_target_user_id; // 取自步骤 2
contact.user_name = "";
contact.avatar_url = "";

tc_iot_call_option_t option = {0};
option.media_content = TC_IOT_MEDIA_CONTENT_AUDIO_VIDEO;
option.video_quality = TC_IOT_VIDEO_QUALITY_SD;

tc_iot_error_e rc = tc_iot_av_call(&contact, option, on_call_op);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_av_call failed: %d\\n", rc);
}
注意:
tc_iot_av_call 返回 TC_IOT_ERR_SUCCESS 仅表示呼叫信令已发出,并不代表通话已建立。需等待 on_call_accepted 回调后,通话才算真正接通,此时再开始上推音视频。

步骤 4:处理通话状态回调

呼叫发出后,设备会依次收到以下回调,业务侧需在对应回调中管理本地状态:
回调
业务建议处理
on_call_accepted
通话接通,创建推流通道并开始推送音视频帧步骤5)。
on_call_rejected
对端拒接,清理呼叫状态,恢复待机。
on_call_timeout
无人接听,清理呼叫状态,可重试或提示用户。
on_call_hangup
通话中挂断,销毁推流通道步骤6),停止采集。
示例:状态机式处理
static bool g_in_call = false;
static tc_iot_av_channel_t *g_video_channel = NULL;
static tc_iot_av_channel_t *g_audio_channel = NULL;

static void on_call_accepted(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_accepted: user_id=%s\\n", contact->user_id);
g_in_call = true;
start_call_streams(); // 见步骤 5
}

static void on_call_rejected(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_rejected: user_id=%s\\n", contact->user_id);
g_in_call = false;
}

static void on_call_timeout(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_timeout: user_id=%s\\n", contact->user_id);
g_in_call = false;
}

static void on_call_hangup(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_hangup: user_id=%s\\n", contact->user_id);
g_in_call = false;
stop_call_streams(); // 见步骤 6
}
说明:
建议在收到 on_call_accepted 后再启动编码器与推流,避免对端尚未接听就占用编码资源。on_call_rejectedon_call_timeouton_call_hangup 均表示通话结束,应统一做资源清理。

步骤 5:创建通话推流通道并推送音视频帧

通话接通后(on_call_accepted 触发),创建音视频发送通道并向 SDK 推送编码后的音视频裸流。
tc_iot_av_channel_t *tc_iot_create_video_channel(int channel_id, tc_iot_video_quality_e quality);
tc_iot_av_channel_t *tc_iot_create_audio_channel();

tc_iot_error_e tc_iot_push_video_frame(tc_iot_av_channel_t *av_channel, const tc_iot_video_frame *frame);
tc_iot_error_e tc_iot_push_audio_frame(tc_iot_av_channel_t *av_channel, const tc_iot_audio_frame *frame);

tc_iot_error_e tc_iot_destroy_video_channel(tc_iot_av_channel_t *av_channel);
tc_iot_error_e tc_iot_destroy_audio_channel(tc_iot_av_channel_t *av_channel);
接口
说明
tc_iot_create_video_channel
创建视频发送通道,channel_id 通话场景固定为 0
tc_iot_create_audio_channel
创建音频发送通道,无需传 channel_id
tc_iot_push_video_frame
推送一帧编码后的视频。
tc_iot_push_audio_frame
推送一帧编码后的音频。
tc_iot_destroy_video_channel
销毁视频通道。
tc_iot_destroy_audio_channel
销毁音频通道。
视频帧 tc_iot_video_frame
字段
说明
codec
视频编码格式,H264 , H265 或 MJPEG
type
帧类型,IDR 关键帧或 P 帧。
rotation
画面旋转角度(0/90/180/270)。
width / height
画面宽高(像素)。
data / data_size
编码后视频裸流数据指针与长度。
pts_ms
帧时间戳(毫秒),传0的话由 SDK 内部填写。
音频帧 tc_iot_audio_frame
字段
说明
codec
音频编码格式,推荐 PCMOPUSAACG722
sample_rate
采样率,推荐 16000
channels
声道数,推荐 MONO(单声道)。
frame_duration_ms
单帧时长(毫秒),推荐 20ms
data / data_size
编码后音频裸流数据指针与长度。
pts_ms
帧时间戳(毫秒)。
注意:
设备通常只有一路麦克风采集源,tc_iot_create_audio_channel 建议全局只创建一次,多路复用。
建议推送第一个 IDR 关键帧之后才开始持续推流,以保证对端能快速解码出画面。
示例
static void start_call_streams(void) {
// 视频通道:通话场景 channel_id = 0
g_video_channel = tc_iot_create_video_channel(0, TC_IOT_VIDEO_QUALITY_SD);
// 音频通道:全局唯一
g_audio_channel = tc_iot_create_audio_channel();
// 之后由采集线程持续调用 push 推流
}

// 推送一帧视频(在视频采集/编码线程中调用)
static void push_one_video_frame(void) {
tc_iot_video_frame frame = {0};
frame.codec = TC_IOT_VIDEO_CODEC_H264;
frame.type = TC_IOT_VIDEO_FRAME_TYPE_IDR; // 首帧用 IDR
frame.rotation = TC_IOT_VIDEO_ROTATION_0;
frame.width = 640;
frame.height = 480;
frame.data = encoded_video_buf;
frame.data_size = encoded_video_len;
frame.pts_ms = current_time_ms();
tc_iot_push_video_frame(g_video_channel, &frame);
}

// 推送一帧音频(在音频采集线程中调用)
static void push_one_audio_frame(void) {
tc_iot_audio_frame frame = {0};
frame.codec = TC_IOT_AUDIO_CODEC_PCM;
frame.sample_rate = TC_IOT_AUDIO_SAMPLE_RATE_16000;
frame.channels = TC_IOT_AUDIO_CHANNEL_MONO;
frame.frame_duration_ms = 20;
frame.data = audio_buf;
frame.data_size = audio_len;
frame.pts_ms = current_time_ms();
tc_iot_push_audio_frame(g_audio_channel, &frame);
}

步骤 6:挂断通话

通话过程中,任意一方均可挂断。设备主动挂断调用 tc_iot_av_hangup;对端挂断时设备会收到 on_call_hangup 回调。
无论哪种情况,都应销毁推流通道、停止采集。
tc_iot_error_e tc_iot_av_hangup(const tc_iot_contact_s *contact, tc_iot_av_operation_cb callback);
参数
说明
contact
通话对端,contact.user_id 需与呼叫/接听时一致。
callback
操作结果回调。
示例
static void on_hangup_op(tc_iot_error_e error_code, const char *error_message) {
printf("[twecall] hangup op: code=%d msg=%s\\n", error_code, error_message ? error_message : "");
}

// 主动挂断
tc_iot_contact_s contact = {0};
contact.user_id = g_target_user_id;
tc_iot_av_hangup(&contact, on_hangup_op);

// 销毁通道(主动挂断与收到 on_call_hangup 后均需调用)
static void stop_call_streams(void) {
if (g_video_channel) {
tc_iot_destroy_video_channel(g_video_channel);
g_video_channel = NULL;
}
if (g_audio_channel) {
tc_iot_destroy_audio_channel(g_audio_channel);
g_audio_channel = NULL;
}
}
注意:
tc_iot_av_hangupcallback 用于确认挂断信令发送结果。即使主动挂断,也建议在 on_call_hangupcallback 中统一做通道销毁,避免重复释放。

完整示例代码

以下示例整合上述步骤,演示设备拉取联系人、呼叫小程序、接通后推流、挂断的完整流程。
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tc_iot.h"
#include "tc_iot_av.h"

// ==================== 全局状态 ====================

static bool g_in_call = false;
static tc_iot_av_channel_t *g_video_channel = NULL;
static tc_iot_av_channel_t *g_audio_channel = NULL;
static char g_target_user_id[128] = {0};

// ==================== 工具 ====================

static uint64_t current_time_ms(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

static void start_call_streams(void) {
g_video_channel = tc_iot_create_video_channel(0, TC_IOT_VIDEO_QUALITY_SD);
g_audio_channel = tc_iot_create_audio_channel();
printf("[twecall] call streams started\\n");
}

static void stop_call_streams(void) {
if (g_video_channel) {
tc_iot_destroy_video_channel(g_video_channel);
g_video_channel = NULL;
}
if (g_audio_channel) {
tc_iot_destroy_audio_channel(g_audio_channel);
g_audio_channel = NULL;
}
printf("[twecall] call streams stopped\\n");
}

// ==================== 通话状态回调 ====================

static void on_call_requested(const tc_iot_contact_s *contact, tc_iot_call_option_t option) {
printf("[twecall] on_call_requested: user_id=%s media=%d quality=%d\\n",
contact->user_id, option.media_content, option.video_quality);
// 设备作为被叫,这里选择接听
tc_iot_av_accept(contact, NULL);
}

static void on_call_accepted(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_accepted: user_id=%s\\n", contact->user_id);
g_in_call = true;
start_call_streams();
}

static void on_call_rejected(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_rejected: user_id=%s\\n", contact->user_id);
g_in_call = false;
}

static void on_call_timeout(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_timeout: user_id=%s\\n", contact->user_id);
g_in_call = false;
}

static void on_call_hangup(const tc_iot_contact_s *contact) {
printf("[twecall] on_call_hangup: user_id=%s\\n", contact->user_id);
g_in_call = false;
stop_call_streams();
}

static void on_audio_frame_received(const tc_iot_audio_frame *frame) {
// 实际接入时:将 frame->data 送入音频播放器
}

static void on_video_frame_received(const tc_iot_video_frame *frame) {
// 实际接入时:将 frame->data 送入视频解码器/渲染器
}

static void on_av_op(tc_iot_error_e error_code, const char *error_message) {
printf("[twecall] av op: code=%d msg=%s\\n", error_code, error_message ? error_message : "");
}

// ==================== 获取联系人 ====================

static void make_call(const char *user_id);

static void on_get_contacts(tc_iot_error_e error_code, const char *error_message,
const tc_iot_contact_s *contacts, uint32_t count,
const char *next_cursor) {
printf("[twecall] get_contacts: code=%d count=%u next_cursor=%s\\n",
error_code, count, next_cursor ? next_cursor : "(end)");

if (error_code != TC_IOT_ERR_SUCCESS) {
printf("get_contacts failed: %s\\n", error_message ? error_message : "");
return;
}

for (uint32_t i = 0; i < count; i++) {
printf(" [%u] user_id=%s name=%s\\n", i, contacts[i].user_id,
contacts[i].user_name ? contacts[i].user_name : "");
if (g_target_user_id[0] == '\\0' && contacts[i].user_id) {
strncpy(g_target_user_id, contacts[i].user_id, sizeof(g_target_user_id) - 1);
}
}

// 还有更多联系人,继续翻页
if (next_cursor != NULL) {
tc_iot_av_get_contacts(next_cursor, 50, on_get_contacts);
return;
}

// 拉取完成,呼叫第一个联系人
if (g_target_user_id[0] != '\\0') {
make_call(g_target_user_id);
} else {
printf("[twecall] no contact to call\\n");
}
}

// ==================== 发起呼叫 ====================

static void make_call(const char *user_id) {
printf("[twecall] calling user_id=%s\\n", user_id);

tc_iot_contact_s contact = {0};
contact.user_id = user_id;
contact.user_name = "";
contact.avatar_url = "";

tc_iot_call_option_t option = {0};
option.media_content = TC_IOT_MEDIA_CONTENT_AUDIO_VIDEO;
option.video_quality = TC_IOT_VIDEO_QUALITY_SD;

tc_iot_error_e rc = tc_iot_av_call(&contact, option, on_av_op);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("[twecall] tc_iot_av_call failed: %d\\n", rc);
}
}

// ==================== 挂断 ====================

static void hangup_call(void) {
if (!g_in_call) {
return;
}
tc_iot_contact_s contact = {0};
contact.user_id = g_target_user_id;
tc_iot_av_hangup(&contact, on_av_op);
}

// ==================== main ====================

int main(int argc, char **argv) {
// 1) 初始化 + 登录(参考快速接入文档)
// ... tc_iot_init / tc_iot_login ...

// 2) 注册通话事件回调
tc_iot_av_observer_s observer;
memset(&observer, 0, sizeof(observer));
observer.on_call_requested = on_call_requested;
observer.on_call_accepted = on_call_accepted;
observer.on_call_rejected = on_call_rejected;
observer.on_call_timeout = on_call_timeout;
observer.on_call_hangup = on_call_hangup;
observer.on_audio_frame_received = on_audio_frame_received;
observer.on_video_frame_received = on_video_frame_received;

tc_iot_error_e rc = tc_iot_av_init(&observer);
if (rc != TC_IOT_ERR_SUCCESS) {
printf("tc_iot_av_init failed: %d\\n", rc);
return -1;
}

// 3) 拉取联系人,回调中自动发起呼叫
tc_iot_av_get_contacts(NULL, 50, on_get_contacts);

// 4) 通话结束后挂断
// hangup_call();

// 5) 反初始化
tc_iot_av_deinit();
return 0;
}
说明:
1. 示例中省略了 tc_iot_init / tc_iot_login 的细节,请参考 登录与注册 完成登录。
2. 实际接入时,音视频帧推送需替换为真实摄像头采集与编码逻辑。
3. SDK 自带 iot_video_demo 示例已完整实现上述流程,可参考 src/demo/iot_video_demo/ 目录。
4. 以上仅为单人通话场景演示。监控/通话及呼叫中接听第二个呼入电话等复杂场景,请参考 iot_video_demo

编译并运行

SDK 自带的 iot_video_demo 已集成 TWeCall 完整流程,可直接编译验证:
cd iot

# Linux 平台(CC TRTC,功能全)
bash build.sh --platform=linux --trtc=cc

# 将 CC TRTC 动态库加入系统动态库搜索路径
export LD_LIBRARY_PATH=tc_iot_sdk/sdk/lib:$LD_LIBRARY_PATH

# 运行(替换为你的设备三元组)
./build/iot_video_demo -p YOUR_PRODUCT_ID -d YOUR_DEVICE_NAME -s 'YOUR_DEVICE_SECRET'
成功启动后会看到:
tc_iot_av_init success
在另一个终端通过命名管道 /tmp/tciot 发命令:
# 拉取联系人列表
echo "get_contacts -n 50" > /tmp/tciot

# 呼叫小程序联系人(user_id 取自联系人列表)
echo "call -u user_id" > /tmp/tciot

# 挂断
echo "hangup -u user_id" > /tmp/tciot

常见问题

现象
排查建议
get_contacts 回调返回 count=0
确认设备已完成 TWeCall 激活与小程序授权;确认设备已绑定可呼叫联系人。
tc_iot_av_call 返回成功但回调中提示 TWeCall device not active
TWeCall 是增值服务,需要在控制台激活。
单击 TWeCall 激活 查看接口说明。
tc_iot_av_call 返回成功但回调中提示
wechat VoIP API error
检查小程序中设备是否授权 VoIP 音视频通话。
tc_iot_av_call 返回 TC_IOT_ERR_NOT_INITIALIZED
确认已调用 tc_iot_av_init,且 tc_iot_login 成功在线。
tc_iot_av_call 返回成功但长时间无 on_call_accepted
等待 on_call_timeout;确认 user_id 格式正确(modelId/wxAppId/openId);确认小程序端在线且已授权。
on_call_accepted 后对端看不到画面
确认首帧推送的是 IDR 关键帧;确认视频通道已创建且 channel_id=0;确认 width/height 与编码实际尺寸一致。
on_call_accepted 后对端听不到声音
确认音频通道已创建;确认 sample_rate(推荐 16000)、channels(推荐 MONO)配置正确。
多次通话后内存持续增长
确认每次 on_call_hangup 后均调用了 tc_iot_destroy_video_channel / tc_iot_destroy_audio_channel
tc_iot_av_get_contacts 返回 TC_IOT_ERR_INVALID_ARGUMENT
确认 limit 在 (0, 1024] 范围内。

联系我们

如果您在接入或使用过程中有任何疑问或者建议,欢迎 联系我们 提交反馈。