Skip to content

接入或使用客户端诊断能力 ​

在客户端 App 中预留诊断入口,可以让测试人员或获得用户授权的支持人员在问题发生后立即上传 SDK 日志;遇到无声、卡顿、花屏、黑屏或对讲异常时,还可以采集当时的原始音视频数据并随日志上传。

建议在开发阶段就完成日志上传入口。这样在需要平台协助诊断时,可以直接上传同一次复现的日志。原始音视频采集涉及更高的数据敏感度,可以根据产品的支持需求按需接入。

这些诊断接口用于客户端 SDK。设备端日志需要按接入 C SDK中的方式由设备保存或回传。

选择诊断方式 ​

根据是否需要把诊断功能接入自己的客户端选择方式:

诉求使用方式
在自己的 App 中长期保留诊断入口接入 SDK 提供的诊断接口,由 App 控制采集与上传
临时复现并把数据交给技术支持,不修改自己的 App直接使用预编译 TiRTC Flutter Android Demo,在 Demo 中连接设备、复现问题并上传

两种方式最终都会返回日志 ID。普通日志不包含原始音视频;原始音视频诊断数据是 SDK 在播放和对讲过程中记录的音视频数据,可能仍是编码数据,不是可以直接播放的 MP4,也不能替代业务录制。诊断数据可能包含真实音视频内容,采集和上传前必须取得必要授权,并避免录入与问题无关的敏感内容。

在自己的 App 中接入诊断能力 ​

接入日志上传(推荐) ​

日志上传主要用于排查问题,不建议把入口作为面向所有用户的常规功能直接展示。常见做法是在“关于”页、版本号或其他约定位置连续点击、双击或长按,打开一个诊断页面;企业内部版本也可以放在仅测试人员可见的设置项中。

建议诊断页面显示“上传中”状态;上传成功后展示并允许复制 logId,上传失败时展示错误码并允许重试。隐藏入口只是一种交互设计,不是安全控制;应用仍需按照隐私政策告知数据用途,并限制能够触发诊断操作的用户范围。

完成 SDK 初始化后,调用当前客户端 SDK 的日志上传接口:

dart
final ({int code, String? logId}) result = await TiRtcLogging.upload();
if (result.code == 0 && result.logId != null) {
  // 在诊断页面展示并允许复制 result.logId。
}
tsx
const result = await TiRtcLogging.upload();
if (result.code === 0 && result.logId !== null) {
  // 在诊断页面展示并允许复制 result.logId。
}
kotlin
val accepted = TiRtcLogging.upload { code, logId ->
  if (code == 0 && !logId.isNullOrEmpty()) {
    // 在诊断页面展示并允许复制 logId。
  }
}
// accepted == 0 只表示上传任务已开始,最终结果以回调为准。
swift
let accepted = TiRtcLogging.upload { result in
    if result.succeeded, let logId = result.logId {
        // 在诊断页面展示并允许复制 logId。
    }
}
// accepted == 0 只表示上传任务已开始,最终结果以 completion 为准。
ts
const result = await TiRtcLogging.upload();
if (result.code === 0 && result.logId !== undefined) {
  // 在诊断页面展示并允许复制 result.logId。
}
python
log_id = client.upload_logs()
if log_id:
    # 在诊断页面展示并允许复制 log_id。
    pass

问题复现后尽快上传,避免后续运行覆盖相关记录。上传成功后,把 logId、问题发生时间、SDK 版本和最短复现步骤一并提供给技术支持。完整的失败处理和排查材料要求见上传客户端日志。

按需接入原始音视频采集 ​

原始音视频诊断采集接口可以记录播放时收到的音视频,帮助技术支持定位普通日志无法解释的媒体问题。代码示例提供上行音频选项时,还可以记录对讲时发送的音频。

建议把它作为诊断页面中的增强操作:先由用户或支持人员明确选择要采集的 Stream ID,再开始和停止采集,不要在应用启动后默认采集。普通日志不包含原始音视频;只有应用显式完成一次采集后,下一次日志上传才会附带采集文件。

支持采集两类数据:

  • 播放音视频时,设备发送、客户端收到的下行原始编码数据。
  • 对讲时,客户端麦克风采集并发送给设备的上行音频。

调用采集接口 ​

开始前确认连接已经建立,并取得实际使用的下行音频和下行视频 Stream ID;代码示例包含上行音频选项时,再填写本地对讲 Stream ID。至少填写一个实际要采集的 Stream ID 列表;Stream ID 取值为 0..15。

dart
final started = await conn.startRawDump(
  TiRtcRawDumpOptions(
    audioStreamIds: <int>[10],
    videoStreamIds: <int>[11],
    uplinkAudioStreamIds: <int>[14],
  ),
);
if (!started.success || started.data == null) return;

// 复现问题后停止采集。
final stopped = await started.data!.stop();
if (!stopped.success || stopped.data == null) return;
if (!stopped.data!.captureComplete) {
  debugPrint('media capture incomplete: ${stopped.data!.stopReason}');
}

final upload = await TiRtcLogging.upload();
// upload.code == 0 且 logId 非空时提交给技术支持。
tsx
const started = await conn.startRawDump({
  audioStreamIds: [10],
  videoStreamIds: [11],
  uplinkAudioStreamIds: [14],
});
if (!started.success || started.data === null) return;

// 复现问题后停止采集。
const stopped = await started.data.stop();
if (!stopped.success || stopped.data === null) return;
if (!stopped.data.captureComplete) {
  console.warn(`media capture incomplete: ${stopped.data.stopReason}`);
}

const upload = await TiRtcLogging.upload();
// upload.code === 0 且 logId 非空时提交给技术支持。
kotlin
val started = conn.startRawDump(
  TiRtcRawDumpOptions(
    audioStreamIds = intArrayOf(10),
    videoStreamIds = intArrayOf(11),
    uplinkAudioStreamIds = intArrayOf(14),
  ),
)
val capture = started.dump ?: return

// 复现问题后停止采集。
val stopped = capture.stop()
val captureFile = stopped.archive ?: return
if (!captureFile.captureComplete) {
  Log.w("TiRTC", "media capture incomplete: ${captureFile.stopReason}")
}

TiRtcLogging.upload { code, logId ->
  // code == 0 且 logId 非空时提交给技术支持。
}
swift
let started = await conn.startRawDump(
    options: TiRtcRawDumpOptions(
        audioStreamIds: [10],
        videoStreamIds: [11],
        uplinkAudioStreamIds: [14]
    )
)
guard let capture = started.dump else { return }

// 复现问题后停止采集。
let stopped = await capture.stop()
guard let captureFile = stopped.archive else { return }
if !captureFile.captureComplete {
    print("media capture incomplete: \(captureFile.stopReason)")
}

TiRtcLogging.upload { result in
    // result.succeeded 时提交 result.logId。
}
ts
const started = await conn.startRawDump({
  audioStreamIds: [10],
  videoStreamIds: [11],
  uplinkAudioStreamIds: [14],
});
if (!started.success || started.data === null) return;

// 复现问题后停止采集。
const stopped = await started.data.stop();
if (!stopped.success || stopped.data === null) return;
if (!stopped.data.captureComplete) {
  console.warn(`media capture incomplete: ${stopped.data.stopReason}`);
}

const upload = await TiRtcLogging.upload();
// upload.code === 0 且 logId 非空时提交给技术支持。
python
options = tirtc.RawDumpOptions(
    audio_stream_ids=(10,),
    video_stream_ids=(11,),
)

with conn.start_raw_dump(options) as capture:
    # 复现问题后停止采集。
    archive = capture.stop()

if not archive.capture_complete:
    print(f"media capture incomplete: {archive.stop_reason}")

log_id = client.upload_logs()
# 把 log_id 提交给技术支持。

单次采集最长 5 分钟,采集文件上限为 1 GiB;达到限制、来源关闭、资源不足或写入失败时会自动停止。停止成功后,仍需检查采集是否完整、停止原因以及未保存的包数和字节数;结果标记为空表示没有采到匹配的数据。通常先检查填写的 Stream ID 是否与实际发送和订阅一致。

TiRTC 和 Ti 云存共用同一套诊断采集资源,同一进程同时只能进行一个原始音视频采集任务。先停止采集,再上传日志;采集和上传并发时会返回资源正在使用。下一次成功采集或成功上传可能清理旧采集文件,需要长期保留时,在开始下一次采集或上传前把文件复制到应用目录。上传失败时,采集文件保留在 SDK 缓存目录,可在网络恢复后重试;成功上传后 SDK 会删除该文件。

使用预编译 Flutter Android Demo 采集 ​

Demo 已经集成原始音视频诊断采集和日志上传,不需要修改客户端代码。它会把采集数据和运行日志一起上传,并返回日志 ID,供技术支持分析。

开始前准备 ​

  • 一台 Android 手机和可以复现问题的设备。
  • TiRTC app_id、设备的 remote_id 和连接 Token。可以通过 TiRTC DevTools CLI生成一次性 Token,也可以启动 Token 签发 HTTP 服务,让手机在连接前动态获取 Token。
  • 设备实际发送的音频、视频 Stream ID。
  • 排查对讲时,还要确认设备订阅的对讲 Stream ID,以及设备是否需要先收到业务控制命令。

Stream ID 标识同一条连接、一个发送方向中的一路流,取值范围是 0..15。同一方向、同一路流的发送端编号必须与接收端绑定和订阅一致;同一发送方向内,不同音视频流使用不同编号,音频和视频不能共用编号。相反方向使用独立的编号空间,可以复用相同数值。因此,采集问题时要分别记录“设备端 → 客户端”的下行音视频 Stream ID 和“客户端 → 设备端”的对讲 Stream ID,不要只记录一个未标方向的数字。

前往下载 API 示例,在 Flutter 卡片中下载并安装 Android APK。

单次最多采集 5 分钟。

连接设备 ​

  1. 打开 Demo,选择 Ti RTC。
  2. 在「连接」中填写 app_id 和 remote_id。除非技术支持提供了专用地址,否则 endpoint 留空。
  3. 在「鉴权」中填写「一次性连接 Token」,或者在「TiRTC DevTools 服务地址」中填写 CLI 输出的「Token 签发服务地址」。
  4. 在「媒体」中填写设备实际发送的音频和视频 Stream ID。最多可以填写 3 路视频;不需要接收的媒体类型留空。
  5. 点击「开始连接、拉流播放」,确认已经连接到目标设备并进入播放页。

采集下行音视频 ​

  1. 确认设备正在发送需要分析的音频或视频。
  2. 点击画面左侧的圆形「抓数据」按钮。按钮变为「结束上传」后,按实际操作复现问题。
  3. 问题出现后点击「结束上传」。按钮会依次显示「打包中」和「上传中」。
  4. 出现「日志上传成功」对话框后,复制日志 ID。

配置的下行 Stream ID 必须与设备发送时使用的编号一致。编号不一致时,即使连接成功,Demo 也收不到或采集不到对应数据。

采集对讲音频 ​

对讲通常在播放页进行。采集期间,Demo 会同时记录当前的下行音视频和 Android 手机麦克风的上行音频。

  1. 打开「偏好设置」,确认「传输 Stream ID」与设备订阅的对讲 Stream ID 一致。
  2. 返回播放页,点击「抓数据」。
  3. 如果设备收到业务控制命令后才订阅对讲流,点击右上角的「命令」,发送双方约定的命令。设备建立连接后已经订阅时,跳过此步。
  4. 点击「启动麦克风」,并在首次使用时允许麦克风权限。
  5. 按实际操作复现问题。需要同时分析下行数据时,让设备继续发送对应的音频或视频。
  6. 点击「结束上传」,等待上传完成并复制日志 ID。

需要控制命令时,Demo 不会自动发送。命令 ID、内容格式和内容由设备业务协议定义,具体操作见收发命令消息。

上传失败后重试 ​

上传失败时,采集数据会保留在手机上。网络恢复后点击「重试上传」,无需重新复现。

提交排查信息 ​

联系技术支持时,请提供:

  • 日志 ID。
  • 问题现象、最短复现步骤和大致发生时间。
  • 设备型号,以及本次使用的下行音视频 Stream ID、传输 Stream ID 和编码格式。

不要在工单、聊天记录或截图中提交连接 Token、SecretKeyId、device_secret_key 等敏感信息。

TiRTC 开发文档