Nano 接入指南
示例仓库见 tirtc-example-nano。
前置条件
开始前先准备好下面这些信息:
| 项目 | 用途 |
|---|---|
| 目标平台信息 | 告诉我们你的目标平台、芯片架构和运行环境 |
| 工具链 | 非常见平台时,用于由我们编译对应平台库 |
license | 设备身份凭证,格式为 "<device_id>,<key>" |
获取 SDK
TiRTC Nano 默认由我们负责编译和交付。
你需要:
- 告诉我们你的目标平台信息
- 拿到我们交付给你的 Nano SDK
如果你的平台比较常见,我们会直接交付对应平台库;如果你的平台有特殊工具链或 sysroot 约束,再由你提供必要编译环境,我们负责编译。
集成 SDK
把我们交付的头文件和库接入你的设备工程。
示例工程、构建脚本和后续接入样例,优先参考 tirtc-example-nano 仓库。
下面给出一个最小 Linux 编译示意:
gcc -o device_app device_app.c \
-I/path/to/TIRTC_NANO/include/tirtc \
-L/path/to/TIRTC_NANO/lib \
-lTiRTC -lTGTRP -lpthread实际集成以交付包和配套构建说明为准。
准备回调和设备侧启动参数
设备侧启动前,至少先准备好下面这些内容:
- 传给
TiRtcStart()的TIRTCCALLBACKS service_entry:设备启动后要访问的 TiRTC 服务入口license:设备身份凭证,格式为"<device_id>,<key>"
如果你的设备还需要控制资源或联网方式,再按需准备这些可选参数:
TIRTC_OPT_MAX_CONNECTIONSTIRTC_OPT_NETWORK_TYPETIRTC_OPT_ICCIDTIRTC_OPT_WAKEUPTIRTC_OPT_RESTRICTED_NETWORKTIRTC_OPT_MAX_SEND_BUFFER
其中有两个时序约束要单独记住:
- 如果使用
TIRTC_OPT_NETWORK_TYPE且设为TIRTC_NETCONN_4G,TIRTC_OPT_ICCID就是必填项 - 如果要修改
TIRTC_OPT_MAX_SEND_BUFFER,必须在TiRtcInit()之前设置
注意:
- 设备侧调用
TiRtcStart()启动服务时不需要token - 只有当当前进程还要主动调用
TiRtcConnect()去连接别的设备时,才需要token - 传给
TiRtcStart()的TIRTCCALLBACKS不能指向临时变量
初始化并启动设备服务
设备端按这个顺序启动即可:
- 先准备好传给
TiRtcStart()的TIRTCCALLBACKS - 调用
TiRtcInit(),再调用TiRtcLogConfig()/TiRtcLogSetLevel()和TiRtcSetOpt(),设置日志、service_entry等启动参数 - 调用
TiRtcStart(license, &callbacks)启动设备服务,并等待on_event(TiEVENT_SYS_STARTED, ...)
下面是一段可直接对照接入的最小设备端骨架:
#include <stdio.h>
#include <string.h>
#include "tiRTC.h"
static void on_event(int event, const void *data, int len)
{
(void)data;
(void)len;
if (event == TiEVENT_SYS_STARTED) {
printf("TiRTC started\n");
} else if (event == TiEVENT_SYS_STOPPED) {
printf("TiRTC stopped\n");
}
}
static void on_conn_accepted(tirtc_conn_t hconn)
{
printf("accepted conn=%p\n", (void *)hconn);
}
static void on_conn_error(tirtc_conn_t hconn, int error)
{
printf("conn error conn=%p error=%d\n", (void *)hconn, error);
}
static const TIRTCCALLBACKS k_callbacks = {
.on_event = on_event,
.on_conn_accepted = on_conn_accepted,
.on_conn_error = on_conn_error,
};
int start_device(const char *service_entry, const char *license)
{
int ret = TiRtcInit();
if (ret != 0) {
return ret;
}
TiRtcLogConfig(1, NULL, 0);
TiRtcLogSetLevel(5);
TiRtcSetOpt(TIRTC_OPT_SERVICE_ENTRY, service_entry, (uint32_t)strlen(service_entry));
ret = TiRtcStart(license, &k_callbacks);
if (ret != 0) {
TiRtcUninit();
return ret;
}
return 0;
}注意:
TIRTC_OPT_MAX_SEND_BUFFER是特例,如果你要改它,要在TiRtcInit()之前调用TiRtcSetOpt()license的格式是"<device_id>,<key>"- 传给
TiRtcStart()的TIRTCCALLBACKS不能指向临时变量 TiRtcStart()返回0只表示参数通过初步检查,真正启动成功要等TiEVENT_SYS_STARTED
在 on_conn_accepted() 中建立连接上下文
设备上线后,远端查看端会主动连接这台设备。设备侧收到新连接时,入口回调是 on_conn_accepted()。
从这个回调开始,这条连接进入应用自己的业务处理流程。你通常要在这里完成三件事:
- 按需为这条连接创建自己的上下文
- 用
TiRtcConnSetUserData()把上下文挂到hconn上 - 关联这条连接需要的业务状态,例如发送线程、流开关状态、待应答命令队列
流控方式由业务自己决定:要么等对端发出 on_request_video() / on_request_audio() 之后再送流,要么连接建立后默认开始送流。
typedef struct {
tirtc_conn_t hconn;
int video_enabled[4];
int audio_enabled[4];
} DeviceConnCtx;
static void on_conn_accepted(tirtc_conn_t hconn)
{
DeviceConnCtx *ctx = calloc(1, sizeof(*ctx));
ctx->hconn = hconn;
TiRtcConnSetUserData(hconn, ctx);
}
static void on_disconnected(tirtc_conn_t hconn)
{
DeviceConnCtx *ctx = (DeviceConnCtx *)TiRtcConnGetUserData(hconn);
free(ctx);
}发送音视频
设备把媒体数据按 Nano 的帧接口发送给 SDK。
先约定好 stream_id
stream_id 的取值范围是 0~15。它在同一条连接内是全局唯一的,音频和视频不能重号。你需要先和查看端约定每一路视频流、音频流分别使用哪个 stream_id,再按这个约定收发媒体数据。
选择发送方式
发送方式有两种,按你的业务形态选一种:
- 固定发送:连接建立后,设备直接开始发送约定好的音视频流
- 按需发送:查看端通过
on_request_video()、on_release_video()、on_request_audio()、on_release_audio()通知设备开始或停止某一路流,on_request_iframe()用来请求下一帧关键帧
例如,你可以约定主视频 0、子视频 1、主音频 2、对讲音频 3。也可以按自己的业务约定其他编号,只要同一条连接里不重号即可。
发送媒体帧
下面是一个最小 H.264 发送示例:
static int send_h264_frame(tirtc_conn_t hconn,
uint8_t stream_id,
const void *data,
uint32_t len,
uint32_t timestamp_ms,
int is_key_frame)
{
TIRTCFRAMEINFO fi;
memset(&fi, 0, sizeof(fi));
fi.stream_id = stream_id;
fi.media = TIRTC_VIDEO_H264;
fi.flags = is_key_frame ? TIRTC_FRAME_FLAG_KEY_FRAME : 0;
fi.ts = timestamp_ms;
fi.length = len;
return TiRtcSendVideo(hconn, &fi, data);
}媒体发送时有几个规则不能踩:
- 某一路视频一旦开始发送,建议先立刻送出一个 I 帧;如果收到了
on_request_iframe(),也应尽快补一个 I 帧 - 每路视频流的第一帧必须是关键帧
fi.length只填写 payload 长度,不包含TIRTCFRAMEINFO自身- 音频帧的
flags填采样规格,而不是关键帧标记 - 如果返回
TIRTC_E_BUSY,说明发送缓冲区已满,需要限流、降帧或等待,而不是继续堆送
收发命令和流内消息
除了音视频,Nano 还提供两类常用数据通路:
- 命令通道:适合状态查询、控制指令、请求-应答
- 流内消息:适合跟随某一路媒体时间线的轻量消息
命令通道
命令通道的数据通过 on_data() 收进来。收到命令后,先区分“请求”还是“应答”,再分别处理。命令 ID 的低 15 位由应用自己定义,但 0x1000 以下保留给 SDK 内部使用。请求-应答场景下,正确顺序是:先拿 SN,先登记等待项,再发送命令。
#define CMD_QUERY_STREAM 0x1004
static void request_stream_info(tirtc_conn_t hconn, uint8_t stream_id)
{
uint32_t sn = atomic_get_cmd_sn();
register_pending(sn, CMD_QUERY_STREAM);
TiRtcSendReqWithSn(hconn, sn, CMD_QUERY_STREAM, &stream_id, sizeof(stream_id));
}流内消息
流内消息通过 TiRtcSendMedia() 发送,只是把 media 设成 TIRTC_MEDIA_MESSAGE。
static void send_stream_message(tirtc_conn_t hconn,
uint8_t stream_id,
const char *text,
uint32_t timestamp_ms)
{
TIRTCFRAMEINFO fi;
memset(&fi, 0, sizeof(fi));
fi.stream_id = stream_id;
fi.media = TIRTC_MEDIA_MESSAGE;
fi.ts = timestamp_ms;
fi.length = (uint32_t)strlen(text);
TiRtcSendMedia(hconn, &fi, text);
}断开连接并释放资源
设备侧的收尾动作按这个顺序处理:
- 某条连接出错或业务结束时,先停掉这条连接上的发送线程或其他会继续使用
hconn的工作线程 - 再调用
TiRtcDisconnect(hconn)做本地收尾;这是异步调用,返回后就不要再继续使用这个hconn - 在
on_disconnected()或你自己的收尾路径里释放连接上下文 - 设备整体退出时,调用
TiRtcStop(),并等待TiEVENT_SYS_STOPPED - 最后调用
TiRtcUninit()
这里的关键约束是:先让连接级业务线程退出,再调用 TiRtcDisconnect();并且在 TiRtcDisconnect() 返回后,就不要再在这个 hconn 上继续做操作。
日志与排查
接入前先确定日志的落盘、导出和回传方式。
日志输出有两种常见方式:
- Linux 设备:调用
TiRtcLogConfig()输出到文件 - 小系统或 RTOS:调用
TiRtcLogSetCallback()接管日志;设置回调后 SDK 不再自己输出日志
TiRtcLogConfig(0, "/tmp/tirtc.log", 512 * 1024);
TiRtcLogSetLevel(5);排查时先看:
- 是否收到
TiEVENT_SYS_STARTED - 是否触发
on_conn_accepted() - 是否收到了
on_request_video()/on_request_audio(),却没有开始发送对应流 - 发送侧是否频繁返回
TIRTC_E_BUSY - 连接出错后是否继续误用已经失效的
hconn
下一步
- 想按对象和方法查 API,请继续阅读 TiRTC Nano API Reference