TiRTC Nano Integration Guide
Example repository: https://github.com/tangeai/tirtc-example-nano
Prerequisites
Prepare the following information before you start:
| Item | Purpose |
|---|---|
| Target platform information | Tells us the target platform, chip architecture, and runtime environment |
| Toolchain | Required when your target platform is uncommon and needs a custom build |
service_entry | The TiRTC service entry URL |
license | The device identity credential, formatted as "<device_id>,<key>" |
| Logging strategy | Linux can log to files; small systems can take over logging via callback |
For common platforms, we can compile and deliver the Nano SDK directly.
Get the SDK
TiRTC Nano is usually built and delivered by us.
You need to:
- Tell us your target platform details
- Obtain the Nano SDK package we deliver for that target
For common platforms, we deliver the corresponding library directly. If your platform has special toolchain or sysroot requirements, you provide the required build environment and we handle the build.
Integrate the SDK
Integrate the delivered headers and libraries into your device project.
For sample projects, build scripts, and follow-up integration examples, use the tirtc-example-nano repository as the primary reference.
Here is a minimal Linux build example:
gcc -o device_app device_app.c \
-I/path/to/TIRTC_NANO/include/tirtc \
-L/path/to/TIRTC_NANO/lib \
-lTiRTC -lTGTRP -lpthreadUse the delivered package and its build instructions as the source of truth for real integration.
Prepare the Callbacks and Device-Side Startup Parameters
Before the device starts, prepare at least the following:
- The
TIRTCCALLBACKSpassed toTiRtcStart() service_entry: the TiRTC service entry the device connects to after startuplicense: the device identity credential, formatted as"<device_id>,<key>"
If your device also needs resource control or network-specific behavior, prepare these optional settings as needed:
TIRTC_OPT_MAX_CONNECTIONSTIRTC_OPT_NETWORK_TYPETIRTC_OPT_ICCIDTIRTC_OPT_WAKEUPTIRTC_OPT_RESTRICTED_NETWORKTIRTC_OPT_MAX_SEND_BUFFER
There are two timing constraints worth keeping in mind:
- If you use
TIRTC_OPT_NETWORK_TYPEand set it toTIRTC_NETCONN_4G,TIRTC_OPT_ICCIDbecomes mandatory - If you need to change
TIRTC_OPT_MAX_SEND_BUFFER, you must set it beforeTiRtcInit()
Notes:
- The device does not need a
tokenwhen it callsTiRtcStart() - A
tokenis needed only if this process also actively callsTiRtcConnect()to connect to another device - The
TIRTCCALLBACKSpassed toTiRtcStart()must not point to a temporary variable
Initialize and Start the Device Service
Start the device in this order:
- Prepare the
TIRTCCALLBACKSthat will be passed toTiRtcStart() - Call
TiRtcInit(), then callTiRtcLogConfig()/TiRtcLogSetLevel()andTiRtcSetOpt()to configure logging and startup parameters such asservice_entry - Call
TiRtcStart(license, &callbacks)to start the device service, then wait foron_event(TiEVENT_SYS_STARTED, ...)
The following minimal device-side skeleton can be used as a direct reference:
#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;
}Notes:
TIRTC_OPT_MAX_SEND_BUFFERis a special case. If you need to change it, callTiRtcSetOpt()beforeTiRtcInit()licensemust use the format"<device_id>,<key>"- The
TIRTCCALLBACKSpassed toTiRtcStart()must not point to a temporary variable - A return value of
0fromTiRtcStart()only means the parameters passed basic validation. Actual startup success is confirmed afterTiEVENT_SYS_STARTED
Initialize Connection Context in on_conn_accepted()
After the device is online, a remote viewer connects to it actively. When the device receives a new connection, the entry callback is on_conn_accepted().
From this callback onward, that connection enters your application's business flow. In practice, you usually do three things here:
- Create your own per-connection context when needed
- Attach that context to
hconnthroughTiRtcConnSetUserData() - Associate the connection with the business state it needs, such as sender threads, stream-enable state, or pending-command state
Stream-control behavior is up to your business flow: you can wait for on_request_video() / on_request_audio() before sending, or you can start sending by default right after the connection is accepted.
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);
}Send Audio and Video
The device sends media data to the SDK through the Nano frame-based interfaces.
Agree on stream_id First
stream_id ranges from 0 to 15. It must be globally unique within the same connection. Audio and video cannot reuse the same value. Before sending and receiving media, agree with the viewer on the stream_id used by each video stream and audio stream.
Choose the Send Strategy
There are two send strategies. Choose the one that matches your product behavior:
- Fixed sending: the device starts sending the agreed streams immediately after the connection is established
- On-demand sending: the viewer uses
on_request_video(),on_release_video(),on_request_audio(), andon_release_audio()to tell the device when to start or stop a stream, and useson_request_iframe()to request the next keyframe
For example, you can use this stream_id layout: main video 0, sub video 1, main audio 2, intercom audio 3. You can also define a different layout as long as the IDs remain unique within one connection.
Send Media Frames
Here is a minimal H.264 send example:
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);
}Do not violate these media-send rules:
- Once a video stream starts sending, it is recommended to send an I-frame immediately; if you receive
on_request_iframe(), you should also provide an I-frame as soon as possible - The first frame of each video stream must be a keyframe
fi.lengthmust contain only the payload length, not the size ofTIRTCFRAMEINFO- For audio frames,
flagsmust carry the sample format rather than a keyframe flag - If the function returns
TIRTC_E_BUSY, the send buffer is full; apply rate limiting, reduce frames, or wait instead of continuing to push more data
Send and Receive Commands and In-Stream Messages
In addition to audio and video, Nano provides two common data paths:
- Command channel: suitable for state queries, control instructions, and request-response exchanges
- In-stream messages: suitable for lightweight messages aligned with the media timeline of a specific stream
Command Channel
Command-channel packets are received through on_data(). After a packet arrives, first decide whether it is a request or a response, then dispatch accordingly.
The lower 15 bits of the command ID are defined by your application, but values below 0x1000 are reserved for internal SDK use. In request-response scenarios, the correct order is: obtain the SN, register the pending item, and then send the command.
#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));
}In-Stream Messages
In-stream messages are sent through TiRtcSendMedia() with media set to 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);
}Disconnect and Release Resources
Handle device-side teardown in this order:
- When a connection errors out or the business flow ends, stop the send thread or any other worker thread that may still use
hconn - Call
TiRtcDisconnect(hconn)for local cleanup; this call is asynchronous, and once it returns you must stop using thathconn - Release the connection context in
on_disconnected()or in your own teardown path - When the device exits as a whole, call
TiRtcStop()and wait forTiEVENT_SYS_STOPPED - Finally call
TiRtcUninit()
The key rule here is: stop per-connection worker threads first, then call TiRtcDisconnect(). Once TiRtcDisconnect() returns, do not continue operating on that hconn.
Logs and Troubleshooting
Decide how logs are stored, exported, and uploaded before integration starts.
Two common logging patterns are:
- Linux devices: call
TiRtcLogConfig()to write logs to a file - Small systems or RTOS: call
TiRtcLogSetCallback()to take over log output; once the callback is set, the SDK stops writing logs by itself
TiRtcLogConfig(0, "/tmp/tirtc.log", 512 * 1024);
TiRtcLogSetLevel(5);When troubleshooting, start with these checks:
- Did you receive
TiEVENT_SYS_STARTED - Did
on_conn_accepted()fire - Did you receive
on_request_video()oron_request_audio()but fail to start sending the corresponding stream - Is the sender frequently returning
TIRTC_E_BUSY - After a connection error, is your code still trying to use an invalid
hconn
Next
- To look up objects and methods, continue to TiRTC Nano API Reference