Integration Model
To run the first end-to-end path, you only need three pieces:
- the device side sends audio and video after starting with a
license - your business backend issues a short-lived
tokenfor a targetpeer_id - the client side connects with
peer_id + tokenand plays the remote media
Prepare These Three Inputs First
| Input | Purpose |
|---|---|
license | Device identity in the format <peer_id>,<device_secret_key>. The device uses it to start and come online. |
peer_id | The connection identifier of the device, for example AABBCCDDEEFF. The client and backend both operate around it. |
token API | Before an Android client connects, it requests one short-lived connection credential from your backend. |
1. Your Backend Returns a Token
The client first sends an authorization request:
http
GET /connect-token?peer_id=device://AABBCCDDEEFF
Authorization: Bearer <user_access_token>Your backend checks whether this user is allowed to connect to the target device:
go
if !canConnectDevice(userID, peerID) {
return http403("forbidden")
}If the check passes, the backend issues a short-lived token:
go
func issueConnectToken(userID string, peerID string) string {
now := time.Now().Unix()
payload := map[string]any{
"sub": userID,
"scope": "connect:" + peerID,
"iss": accessID,
"iat": now,
"exp": now + 300,
"nonce": randomNonce(),
}
payloadB64 := base64url(jsonMarshal(payload))
deviceSig := hmacSHA256(deviceSecretKey, payloadB64)
appSig := hmacSHA256(secretKey, payloadB64+"."+deviceSig)
return "v1." + payloadB64 + "." + appSig
}Then return it through the HTTP response:
json
{
"peer_id": "device://AABBCCDDEEFF",
"token": "v1.xxxxxx.yyyyyy",
"expires_in": 300
}2. The Device Starts and Sends Media
Initialize the SDK and start the device:
c
TiRtcInit();
TiRtcSetOpt(TIRTC_OPT_SERVICE_ENTRY, service_entry, (uint32_t)strlen(service_entry));
static const TIRTCCALLBACKS callbacks = {
.on_event = on_event,
.on_conn_accepted = on_conn_accepted,
};
TiRtcStart(license, &callbacks);Assemble and send H264 video frames:
c
TIRTCFRAMEINFO vfi;
memset(&vfi, 0, sizeof(vfi));
vfi.stream_id = 0;
vfi.media = TIRTC_VIDEO_H264;
vfi.flags = is_key_frame ? TIRTC_FRAME_FLAG_KEY_FRAME : 0;
vfi.ts = video_timestamp_ms;
vfi.length = video_len;
TiRtcSendMedia(hconn, &vfi, video_data);Assemble and send G711A audio frames:
c
TIRTCFRAMEINFO afi;
memset(&afi, 0, sizeof(afi));
afi.stream_id = 1;
afi.media = TIRTC_AUDIO_ALAW;
afi.flags = TIRTC_AUDIOSAMPLE_16K16B1C;
afi.ts = audio_timestamp_ms;
afi.length = audio_len;
TiRtcSendMedia(hconn, &afi, audio_data);3. The Client Connects and Plays
On Android, prepare the playback objects first:
kotlin
val audioOutput = TiRtcAudioOutput()
val videoOutput = TiRtcVideoOutput()
val remoteVideoContainer = findViewById<FrameLayout>(R.id.remote_video_container)Before connecting, declare the render host and the remote consume routes:
kotlin
val conn = TiRtcConn()
videoOutput.attachView(remoteVideoContainer)
audioOutput.attach(conn, streamId = 1)
videoOutput.attach(conn, streamId = 0)
conn.connect(peerId, token)These are three separate actions:
videoOutput.attachView(...)only binds the local render hostaudioOutput.attach(...)andvideoOutput.attach(...)only declare which remote routes to consume on the connectionconn.connect(...)only establishes the transport connection
They are intentionally decoupled. TiRtcConn no longer owns the public audio-video attach or detach APIs.
Next
- Want to try the ready-made experience first: see Out-of-Box Experience
- Want to issue tokens from your backend: see Connection
- Want to integrate the device side: see Nano SDK Integration Guide
- Want to integrate the Android client side: see Android SDK Integration Guide