Skip to content

设备端集成

设备端集成的目标是使用业务服务端下发的 peer_idtoken 建立 TiRTC 连接,然后完成音频上下行、房间事件处理和离房清理。

本页假定设备端已完成 TiRTC SDK 集成。如果尚未集成,请先参阅 TiRTC 设备端集成文档。

前置条件

调用 TiRtcWhipConnect 前,设备端需先完成 TiRTC 设备端启动流程:

  1. 调用 TiRtcInit() 初始化 SDK。
  2. 按 TiRTC 设备端接入要求设置 device_secret_key,并调用 TiRtcStart(device_id, &callbacks) 启动设备端。
  3. 等待 TIRTC_EVENT_SYS_STARTED 后,再调用 TiRtcWhipConnect(peer_id, token, ...) 加入 Room。

TiRTC 启动流程见 连接设备端

建连步骤

  1. 完成 TiRTC 初始化和设备端启动,并等待 TIRTC_EVENT_SYS_STARTED
  2. 从业务服务端获取 peer_idtoken
  3. 调用 TiRTC TiRtcWhipConnect,并传入业务服务端下发的 peer_idtoken
  4. 等待 TiRTC 返回连接结果。
  5. 连接完成后发送 join_room,验证通过后再开始发送上行音频。
  6. 监听下行音频并按 join_room 响应中的 output_audio 解码播放。
  7. 监听命令通道 0x2200 下发的房间快照和成员事件。
  8. 主动离房时先发送 leave_room,收到房间关闭通知或信令发送完成后进入资源释放流程。

peer_idtoken 都应原样传入 TiRTC TiRtcWhipConnect 接口。设备端不要解析 peer_id,也不要把它改写成其他地址或参数。

建连示例

设备收到 peer_idtoken 后,直接调用 TiRTC SDK 建立连接。TiRtcWhipConnect 返回 0 只表示建连请求已提交;真正成功以回调中的 err == 0 为准。

c
#include "tiRTC.h"
#include <stdbool.h>
#include <string.h>

static tirtc_conn_t g_room_conn = NULL;
static bool g_tirtc_ready = false;

static int room_tirtc_runtime_init(const char *device_id)
{
    if (g_tirtc_ready) {
        return 0;
    }

    int ret = TiRtcInit();
    if (ret != 0) {
        return ret;
    }

    TIRTCCALLBACKS cbs;
    memset(&cbs, 0, sizeof(cbs));
    ret = TiRtcStart(device_id, &cbs);
    if (ret != 0) {
        TiRtcUninit();
        return ret;
    }

    g_tirtc_ready = true;
    return 0;
}

static void on_room_whip(int err, tirtc_conn_t hconn, void *user_data)
{
    (void)user_data;
    if (err != 0) {
        /* 建连失败:记录错误码,释放本地房间状态 */
        return;
    }

    g_room_conn = hconn;
    /* 建连成功后,开始上行音频并监听命令通道 0x2200 */
}

int start_room_tirtc_connect(const char *device_id,
                             const char *peer_id,
                             const char *token)
{
    int ret = 0;

    if (device_id == NULL || device_id[0] == '\0' ||
        peer_id == NULL || peer_id[0] == '\0' ||
        token == NULL || token[0] == '\0') {
        return -1;
    }

    ret = room_tirtc_runtime_init(device_id);
    if (ret != 0) {
        return ret;
    }

    return TiRtcWhipConnect(peer_id, token, on_room_whip, NULL);
}

void stop_room_tirtc_connect(void)
{
    if (g_room_conn != NULL) {
        TiRtcClose(g_room_conn);
        g_room_conn = NULL;
    }

    if (g_tirtc_ready) {
        TiRtcStop();
        TiRtcUninit();
        g_tirtc_ready = false;
    }
}

TiRtcWhipConnect 返回 0 只表示建连请求已提交;真正成功以回调中的 err == 0 为准。设备端应在收到 TIRTC_EVENT_SYS_STARTED 后再发起连接。

发送 join_room

TiRTC 连接建立后,设备必须在 10 秒内通过命令通道 0x2200 发送 JSON-RPC Request。room_iddevice_id 必须与申请凭证时的值一致。

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "join_room",
  "params": {
    "room_id": "room-001",
    "device_id": "device-001",
    "input_audio": {"codec": "pcm", "sample_rate": 16000, "channels": 1},
    "output_audio": {"codec": "g711a", "sample_rate": 16000, "channels": 1}
  }
}

收到成功响应后设备才算正式加入房间。响应会返回 session_id 及规范化后的实际音频格式;身份或格式校验失败会返回 JSON-RPC 错误。

旧的 start_session 不再兼容。TiRTC 建连后必须先完成 join_room,否则不能发送音频或其他房间信令。

支持的音频格式与采样率

当前 Room 设备链路上下行均支持以下格式:

codec采样率(Hz)声道
opus8000160001
pcm8000160001
g711a8000160001
amr8000(AMR-NB)、16000(AMR-WB)1

设备可在 join_room 中为 input_audiooutput_audio 分别指定编码和采样率,两者相互独立。设备端必须使用成功响应回显的实际格式,且发送的 mediaflagsinput_audio 保持一致。

上行音频示例

设备端在采集线程中按固定周期发送音频帧。推荐 20 ms 一帧。

c
#include <stdint.h>
#include <string.h>
#include "tiRTC.h"

static const uint8_t kRoomAudioStreamId = 1;

int room_send_audio_opus(tirtc_conn_t hconn,
                         const void *opus_data,
                         uint32_t len,
                         uint32_t ts_ms)
{
    TIRTCFRAMEINFO fi;
    memset(&fi, 0, sizeof(fi));
    fi.stream_id = kRoomAudioStreamId;
    fi.media = TIRTC_AUDIO_OPUS;
    /* 必须与平台侧配置的上行音频格式保持一致 */
    fi.flags = TIRTC_AUDIOSAMPLE_16K16B1C;
    fi.ts = ts_ms;
    fi.length = len;
    return TiRtcSendAudioStream(hconn, &fi, opus_data);
}

音频参数需要同时满足:

  1. 设备实际采集和编码后的数据格式。
  2. C 语言层设置的 fi.media
  3. C 语言层设置的采样规格 fi.flags

例如:opus/16000/mono 对应 TIRTC_AUDIO_OPUSTIRTC_AUDIOSAMPLE_16K16B1C

下行音频处理

Room 下行音频通过 TiRTC 音频流下发。设备端应按 join_room 响应中的 output_audio 解码并送入播放链路:

  • stream_id 过滤房间下行音频流。
  • 按音频帧的 mediaflags 解码,并与 output_audio 核对。
  • 播放链路应支持连续流式播放,不要等待整段音频缓存完成后再播放。
  • 收到 room_closed 或本地离房时,应及时停止播放并清理缓冲区。

命令通道

Room 使用 TiRTC 命令通道 0x2200 承载 JSON-RPC 2.0 消息:

  • 建连完成后,设备端必须先发送 join_room;入房成功后,Room 服务会下发 room_snapshot 初始化本地成员列表。
  • 主动离房时,设备端必须发送不带 idleave_room Notification。
  • 设备端可发送 get_room_snapshot 主动拉取成员列表。
  • 本端麦克风状态变化时,设备端可发送 set_mic_state 同步 onspeakingoff
  • 收到 participant_joinedparticipant_leftparticipant_mic_state_changed 后更新本地成员状态。
  • 收到 room_closed 后进入资源释放流程。

详细字段见 房间信令

命令通道接收示例

下面示例演示命令字过滤和事件分发方式,其中 jsonrpc_methodmethod_equalshandle_* 函数需要由业务代码基于 JSON 库实现。

c
#include <stdint.h>
#include "tiRTC.h"

#define TIRTC_ROOM_SIGNALING 0x2200

static const char *jsonrpc_method(const void *data, uint32_t len);
static int method_equals(const char *method, const char *expected);
static void handle_room_snapshot(const void *data, uint32_t len);
static void handle_participant_joined(const void *data, uint32_t len);
static void handle_participant_left(const void *data, uint32_t len);
static void handle_participant_mic_state_changed(const void *data, uint32_t len);
static void handle_room_closed(const void *data, uint32_t len);

static void on_room_command(tirtc_conn_t hconn, uint32_t cmdw,
                            const void *data, uint32_t len)
{
    const char *method;
    (void)hconn;

    if (cmdw != TIRTC_ROOM_SIGNALING || data == NULL || len == 0) {
        return;
    }

    /* data 是 UTF-8 JSON-RPC 字符串。生产代码中请使用 JSON 库解析。 */
    method = jsonrpc_method(data, len);
    if (method_equals(method, "room_snapshot")) {
        handle_room_snapshot(data, len);
        return;
    }
    if (method_equals(method, "participant_joined")) {
        handle_participant_joined(data, len);
        return;
    }
    if (method_equals(method, "participant_left")) {
        handle_participant_left(data, len);
        return;
    }
    if (method_equals(method, "participant_mic_state_changed")) {
        handle_participant_mic_state_changed(data, len);
        return;
    }
    if (method_equals(method, "room_closed")) {
        handle_room_closed(data, len);
        return;
    }
}

处理建议:

  • room_snapshot:以快照为准初始化本地成员列表。
  • participant_joined:把新成员加入本地成员列表。
  • participant_left:从本地成员列表移除离开的成员。
  • participant_mic_state_changed:更新指定成员的麦克风状态。
  • set_mic_state:本端麦克风开关或讲话状态变化时发送;它只同步状态,不代替本地采集和上行控制。
  • room_closed:停止采集和播放,并进入资源释放流程。

资源释放

设备端应把主动离房、网络断开、业务超时和房间关闭通知归口到同一个清理流程:

  • 主动离房时,先通过命令通道 0x2200 发送不带 idleave_room Notification。
  • 停止采集和播放。
  • 关闭 TiRTC 连接。
  • 清理本地房间状态和播放缓冲区。
  • 丢弃已过期的 token,下次入房重新申请。

最小跑通路径

为了快速排查环境与配置问题,建议先按以下端到端路径验证:

  1. 服务端请求测试:使用业务服务端调用 POST /v1/token/room,确保能返回有效的 peer_idtoken。完整字段见 服务端接口
  2. 设备端启动测试:确认设备端已完成 TiRtcInit()TiRtcStart(),并收到 TIRTC_EVENT_SYS_STARTED
  3. 设备端连接测试:将 device_idpeer_idtoken 传入 start_room_tirtc_connect,观察 on_room_whip 回调的 err 是否为 0
  4. 入房与快照验证:建连成功后发送 join_room,成功响应后设备端应能在命令通道 0x2200 收到 room_snapshot
  5. 多设备入房验证:让两个设备使用同一个业务 room_id 分别申请凭证并入房,先入房设备应能收到 participant_joined
  6. 麦克风状态验证:一个设备发送 set_mic_state 切换 onspeakingoff,其他在线设备应能收到 participant_mic_state_changed
  7. 音频与离房验证:一个设备持续发送测试音频,另一个设备应能收到下行音频;任一设备发送 leave_room 后,其他在线设备应能收到 participant_left

Room 文档