Ti 云存 React Native API 参考
本文列出 React Native 中 Ti 云存与通用 Media 的公开 API、返回结果和行为约定。第一次接入时,播放录像先阅读查询与播放录像,导出本地 MP4 先阅读下载录像。
同步命令返回 number 错误码,TI_CLOUD_STORAGE_ERROR_OK 表示当前调用成功。异步方法通过 Promise 返回唯一最终结果。常见错误通过包根入口提供公开常量;没有命名常量的错误仍会返回完整整数值,含义和处理建议见错误码。TiCloudStorage.errorToString(code) 只用于展示或诊断。
基础类型
import type { ReactElement } from 'react';
import type { ViewProps } from 'react-native';
export type Resp<T> = Readonly<{
success: boolean;
code: number | null;
message: string | null;
data: T | null;
}>;
export type TiCloudStorageRecordingRange = Readonly<{
startTimeMs: number;
endTimeMs: number;
}>;
export type TiCloudStorageRecordingRangesResult = Readonly<{
code: number;
recordings: ReadonlyArray<TiCloudStorageRecordingRange>;
}>;
export type TiCloudStorageRecordingDay = Readonly<{
date: string;
hasRecording: boolean;
}>;
export type TiCloudStorageRecordingDaysResult = Readonly<{
code: number;
days: ReadonlyArray<TiCloudStorageRecordingDay>;
}>;
export const TiCloudStorageReplaySpeed = {
x0_125: 'x0_125',
x0_25: 'x0_25',
x0_5: 'x0_5',
x1: 'x1',
x2: 'x2',
x4: 'x4',
x8: 'x8',
} as const;
export type TiCloudStorageReplaySpeed =
(typeof TiCloudStorageReplaySpeed)[keyof typeof TiCloudStorageReplaySpeed];| 类型或字段 | 说明 |
|---|---|
TiCloudStorageRecordingRange.startTimeMs | 录像可用时间段的开始时间,UTC Unix 毫秒 |
TiCloudStorageRecordingRange.endTimeMs | 录像可用时间段的结束时间,UTC Unix 毫秒;必须晚于 startTimeMs |
TiCloudStorageRecordingRangesResult.code | 查询最终错误码 |
TiCloudStorageRecordingRangesResult.recordings | 查询得到的录像可用时间段;成功但没有录像时为空数组 |
TiCloudStorageRecordingDay.date | 严格 YYYY-MM-DD 日期 |
TiCloudStorageRecordingDay.hasRecording | 该自然日至少有一个当前可见录像时点时为 true |
TiCloudStorageRecordingDaysResult | 日期查询的最终错误码和完整逐日结果 |
| 倍速 | 说明 |
|---|---|
x0_125 | 1/8 倍速,视频慢放,音频静音 |
x0_25 | 1/4 倍速,视频慢放,音频静音 |
x0_5 | 1/2 倍速,视频慢放,音频静音 |
x1 | 1 倍速,播放音频和视频 |
x2 | 2 倍速,视频播放,音频静音 |
x4 | 4 倍速,视频播放,音频静音 |
x8 | 8 倍速,视频播放,音频静音 |
所有时间必须是 JavaScript 安全整数;SDK 不进行本地时区换算。
使用可用时间段开始回放
selectedRecording 是从 listRecordings 返回结果中选择的一段录像。下面只展示正常调用顺序;回放期间保持 replay、videoOutput 和 audioOutput 有效。
const initCode = await TiCloudStorage.init({appId});
if (initCode !== TI_CLOUD_STORAGE_ERROR_OK) return;
const cloudStorage = new TiCloudStorage(token);
const replay = cloudStorage.createReplay();
const videoOutput = new TiCloudStorageVideoOutput();
const audioOutput = new TiCloudStorageAudioOutput();
replay.onError = showReplayError;
replay.onCompleted = markReplaySourceCompleted;
videoOutput.attach(replay, videoChannelId);
audioOutput.attach(replay, audioChannelId);
replay.play(
selectedRecording.startTimeMs,
selectedRecording.endTimeMs,
);
function CloudStoragePlaybackVideo(): ReactElement {
return videoOutput.view({style: styles.video});
}同步方法返回非 TI_CLOUD_STORAGE_ERROR_OK 时,当前操作没有生效。通过两个 Output 的状态更新播放界面;Token、网络或录像读取失败时,在 Replay 的错误回调中处理。
页面退出时,先停止 Replay、detach 两个 Output,并从组件树中卸载 Video Output 的 Fabric View。随后依次 dispose Output 和 Replay。不再访问这台设备时 dispose cloudStorage;应用不再使用 Ti 云存时,最后调用 TiCloudStorage.shutdown()。
TiCloudStorage
export class TiCloudStorage {
constructor(token: string);
static init(options: Readonly<{
appId: string;
endpoint?: string;
consoleLogEnabled?: boolean;
}>): Promise<number>;
static shutdown(): number;
static errorToString(code: number): string;
updateToken(token: string): number;
listRecordingDays(
startDate: string,
endDate: string,
timeZoneId?: string,
): Promise<TiCloudStorageRecordingDaysResult>;
listRecordings(
startTimeMs: number,
endTimeMs: number,
): Promise<TiCloudStorageRecordingRangesResult>;
createReplay(): TiCloudStorageReplay;
exportRecording(
options: TiCloudStorageExportOptions,
observer?: ((progress: number) => void) | TiCloudStorageExportObserver,
): Resp<TiCloudStorageExportTask>;
dispose(): number;
}Promise resolve 统一结果码,不以 reject 表示 SDK 运行错误。JavaScript 参数类型错误或 React Native 模块不可用时,Promise 仍可能 reject。
使用任何 Ti 云存 API 前先调用 TiCloudStorage.init。初始化后用一台设备的非空 Token 创建 TiCloudStorage 实例;构造不发起网络请求。同时访问多台设备时创建多个实例。释放全部 TiCloudStorage 实例、Replay、Output 和任务后调用 TiCloudStorage.shutdown()。
TiRTC 与 Ti 云存可以在同一进程共存,两者可以使用不同的 App ID 和 endpoint。原生宿主从平台 cache 目录解析出的工作根目录和 consoleLogEnabled 由两者共享,因此配置必须一致。Runtime 在该目录下管理日志、日志归档和临时媒体文件。初始化第二项服务不会重启 Runtime;共享配置不一致时 Promise resolve already-initialized,已初始化的服务不受影响。调用 TiCloudStorage.shutdown() 不会停止 TiRTC。
TiCloudStorage.shutdown() 在尚未初始化或已经关闭时幂等返回成功;仍有活动资源时返回 in-use。在对象回调中同步停止、解绑或释放当前对象及其关联资源,也会返回 in-use 且不改变状态。其他无关实例和资源不受此限制。
cloudStorage.dispose() 只有在底层资源释放成功后才使实例失效。若返回 in-use 或其他错误,实例和当前 Token 仍然有效;清理占用资源后可以重试。SDK 不负责清除调用方持有的 Token 字符串。
单次查询范围最长为 10 天;没有录像时,recordings 为空列表。
List 结果按 startTimeMs 升序排列并裁剪在请求的 [startTimeMs, endTimeMs) 内;重叠或首尾相接的时间段会合并,任何大于 0 的空洞都保留为不同项。结果是设备级完整快照,不保证每个 Channel 都有媒体,也不分页。合并后最多返回 10000 项;查询跨度或结果数量超限时整体 resolve range-too-large,不返回部分结果;成功但没有录像时返回空数组。
exportRecording 同步返回 Task 接受结果。创建成功不表示文件已经生成;通过 TiCloudStorageExportTask.result 等待任务自然完成。
| API | 说明 |
|---|---|
init(options) | 初始化 Ti 云存。appId 必填;endpoint 仅用于自定义服务地址;consoleLogEnabled 默认为 false。Promise 返回初始化错误码 |
shutdown() | 关闭 Ti 云存。调用前必须释放所有 TiCloudStorage 实例、Replay、Output,并等待查询、截图和文件 Task 完成 |
errorToString(code) | 返回错误码对应的稳定名称,用于日志和诊断 |
updateToken(token) | 为同一设备更新 Token,只影响之后启动的 List、Play 和 Export |
listRecordingDays(startDate, endDate, timeZoneId) | 查询包含式日期范围,单次最多 31 天。timeZoneId 使用 IANA ID,省略时默认 Asia/Shanghai;Promise 返回包含 true/false 日期的完整升序结果 |
listRecordings(startTimeMs, endTimeMs) | 查询当前设备指定 UTC 时间范围内的录像可用时间段;单次跨度最长 10 天 |
createReplay() | 创建属于当前设备的 Replay |
exportRecording(options, observer) | 开始一次录像导出;可传简单进度回调或 observer,同步返回 Resp<TiCloudStorageExportTask> |
dispose() | 释放当前设备实例;仍有查询、Replay、ExportTask 或待处理回调时返回 in-use |
Token 过期由触发它的 List、Play 或 Export 直接报告 token-expired。SDK 不另发过期通知,也不自动刷新或重试。应用取得同一设备的新 Token、调用 updateToken,再显式重做失败的操作。SDK 保存不同的非空新 Token,供之后启动的操作使用;重复设置相同 Token 不会清除已经确定的过期状态。访问另一台设备时创建新的 TiCloudStorage。
TiCloudStorageReplay
录像缺口和导出报告使用以下类型:
type TiCloudStorageRecordingRange = Readonly<{
startTimeMs: number;
endTimeMs: number;
}>;
type TiCloudStorageRecordingTrack = Readonly<{
kind: 'video' | 'audio';
channelId: number;
}>;
type TiCloudStorageRecordingGap = Readonly<{
range: TiCloudStorageRecordingRange;
tracks: ReadonlyArray<TiCloudStorageRecordingTrack>;
reasons: ReadonlyArray<
| 'unknown'
| 'notFound'
| 'downloadFailed'
| 'integrityFailed'
| 'mediaUnreadable'
| 'noKeyFrame'
| 'noRecording'
| 'decryptionFailed'
| 'unsupportedMedia'
| 'trackUnavailable'
>;
}>;
type TiCloudStorageExportSegment = Readonly<{
sourceRange: TiCloudStorageRecordingRange;
outputStartMs: number;
outputEndMs: number;
}>;
type TiCloudStorageExportTermination =
| 'exhausted'
| 'interrupted'
| 'cancelled'
| 'failed';export class TiCloudStorageReplay {
readonly speed: TiCloudStorageReplaySpeed;
readonly currentTimeMs: number | null;
onTimeChanged: ((timeMs: number) => void) | null;
onError: ((code: number) => void) | null;
onCompleted: (() => void) | null;
onRecordingGap: ((gap: TiCloudStorageRecordingGap) => void) | null;
private constructor();
play(
startTimeMs: number,
endTimeMs: number,
initialTimeMs?: number,
): number;
pause(): number;
resume(): number;
seek(timeMs: number): number;
setSpeed(speed: TiCloudStorageReplaySpeed): number;
startRecording(
options: TiCloudStorageStartRecordingOptions,
): Resp<TiCloudStorageRecordingTask>;
startRawDump(
options: TiCloudStorageRawDumpOptions,
): Promise<Resp<TiRawDump>>;
stop(): number;
dispose(): number;
}| 成员 | 说明 |
|---|---|
speed | 当前回放倍速,初始值为 x1 |
currentTimeMs | 当前回放位置,UTC Unix 毫秒;首次取得位置前和 stop 后为 null |
onTimeChanged | 回放位置变化通知;晚设置时不会补发之前的事件 |
onError | 当前 play 的 Token、权限、网络或录像读取错误;一次回放最多通知一次 |
onCompleted | 当前 play 的时间范围自然耗尽通知;一次回放最多通知一次 |
onRecordingGap | SDK 确认并越过录像缺口时通知缺口范围、受影响 Channel 和原因;缺口后仍有可解码媒体时继续播放 |
play(startTimeMs, endTimeMs, initialTimeMs) | 播放所属设备的指定 UTC 时间范围;initialTimeMs 可选,省略时从范围开头播放 |
pause() | 暂停当前回放;重复调用成功 |
resume() | 继续当前回放;重复调用成功 |
seek(timeMs) | 定位到当前回放范围内的 UTC 时间;同步返回操作是否被接受 |
setSpeed(speed) | 设置七档固定速度;非 x1 时 Audio Output 静音,慢放画面采用持帧显示 |
startRecording(options) | 保存当前回放的一段内容;同步返回 Resp<TiCloudStorageRecordingTask> |
startRawDump(options) | 采集指定 Channel 的原始音视频数据,用于问题排查 |
stop() | 停止当前回放并清除 currentTimeMs;保留 Output 绑定和倍速 |
dispose() | 释放 Replay;调用后不能继续使用该实例 |
Replay 只能由 cloudStorage.createReplay() 创建,并固定属于该设备实例。Replay 不公开 state。onError 返回当前 play 过程中发生的 Token、权限、网络或录像读取错误。错误发生时恰好调用一次;同步拒绝、主动 stop、被新 play 替换或自然完成不会触发。onCompleted 是 Replay 的自然完成事件:每个被接受的 play 仅在其时间范围自然耗尽时恰好调用一次;同步拒绝、主动 stop、被新 play 替换或错误终止均不触发。开始 play 前设置回调,晚设置不会重放此前错误或完成事件。接受 Play 时固定使用 TiCloudStorage 实例当前的 Token,之后更新 Token 不改变本次回放。
云端明确返回录像文件不存在或文件已被删除时,onError 返回 TI_CLOUD_STORAGE_ERROR_RECORDING_NOT_FOUND;SDK 未能成功下载目标录像文件时,返回 TI_CLOUD_STORAGE_ERROR_RECORDING_DOWNLOAD_FAILED;文件已经下载但 SDK 无法解密或解析出有效音视频帧时,返回 TI_CLOUD_STORAGE_ERROR_RECORDING_UNREADABLE。SDK 无法完成录像查询或取得下载信息时,返回 TI_CLOUD_STORAGE_ERROR_UNAVAILABLE。
currentTimeMs在首次取得位置前和stop后为null;暂停时保留最后位置,自然完成时为请求的endTimeMs,回放失败时保留最后已知位置。晚设置onTimeChanged不重放旧事件,先读取属性再订阅。speed初始为x1;非x1时 Audio Output 静音。play前必须至少绑定一个 Output。onCompleted不取代 Output 的completed:前者表示 Replay 来源自然耗尽,后者只表示对应 Output 已排空。自然完成会自动终结活动 RecordingTask。play要求startTimeMs < endTimeMs;传入的initialTimeMs必须位于[startTimeMs, endTimeMs)。参数校验失败同步返回invalid-argument,原回放不受影响。成功接受后从初始位置建立新回放,不会先输出范围开头的媒体;初始位置落在空洞中时从其后的第一段录像开始,其后没有录像时按自然结束处理。- 最后一个 Output 离开且没有活动 RecordingTask 时,Replay 停止读取来源,但不触发
onCompleted。 - 新
play被接受后原回放停止,但保留 Output 绑定、Audio Output 音量和速度;同步拒绝时原回放不受影响。 pause和resume分别幂等;尚未开始、已经stop、自然完成或失败时返回not-started。- Seek 只能在当前
play仍在运行或暂停时调用,目标必须位于当前[startTimeMs, endTimeMs)。回放未活动或目标越界时,分别同步返回not-started或invalid-argument,当前回放不受影响。自然完成后要从指定位置重新播放时,调用带initialTimeMs的play。 - Seek 保留速度和暂停意图;连续调用时,以最后一次 Seek 的目标为准。定位生效后,
currentTimeMs和onTimeChanged更新为第一个不早于目标的可播放位置。目标合法但其后没有录像时,位置推进到endTimeMs并按自然结束触发onCompleted。 stop幂等,不触发onCompleted,也不把 Output 标记为completed;它会清除currentTimeMs,并保留 Output 绑定和速度。
Audio/Video Output 扩展
export const TiCloudStorageAudioOutputState = {
idle: 'idle',
buffering: 'buffering',
playing: 'playing',
failed: 'failed',
paused: 'paused',
completed: 'completed',
} as const;
export type TiCloudStorageAudioOutputState =
(typeof TiCloudStorageAudioOutputState)[keyof typeof TiCloudStorageAudioOutputState];
export const TiCloudStorageVideoOutputState = {
idle: 'idle',
buffering: 'buffering',
rendering: 'rendering',
failed: 'failed',
paused: 'paused',
completed: 'completed',
} as const;
export type TiCloudStorageVideoOutputState =
(typeof TiCloudStorageVideoOutputState)[keyof typeof TiCloudStorageVideoOutputState];
export type TiCloudStorageVideoOutputViewProps = ViewProps &
Readonly<{
output: TiCloudStorageVideoOutput;
resizeMode?: 'contain' | 'cover' | 'stretch';
}>;
export class TiCloudStorageAudioOutput {
readonly state: TiCloudStorageAudioOutputState;
onStateChanged: ((state: TiCloudStorageAudioOutputState) => void) | null;
onError: ((code: number) => void) | null;
attach(replay: TiCloudStorageReplay, channelId: number): number;
setVolume(volumePercent: number): number;
detach(): number;
dispose(): number;
}
export class TiCloudStorageVideoOutput {
readonly state: TiCloudStorageVideoOutputState;
onStateChanged: ((state: TiCloudStorageVideoOutputState) => void) | null;
onError: ((code: number) => void) | null;
view(
props?: Omit<TiCloudStorageVideoOutputViewProps, 'output'>,
): ReactElement;
attach(replay: TiCloudStorageReplay, channelId: number): number;
takeSnapshot(): Promise<Resp<TiCloudStorageSnapshotFile>>;
detach(): number;
dispose(): number;
}
export function TiCloudStorageVideoOutputView(
props: TiCloudStorageVideoOutputViewProps,
): ReactElement;setVolume 接收 0..100 的安全整数;0 表示静音。它只调整当前 Audio Output,不修改系统全局音量。新一次 play 会保留该值。
Output 状态
| Audio 状态 | Video 状态 | 说明 |
|---|---|---|
idle | idle | 尚未输出媒体,或已经 detach |
buffering | buffering | 正在等待足够的媒体数据 |
playing | rendering | 正在播放音频或渲染视频 |
paused | paused | Replay 已暂停 |
completed | completed | Replay 来源已经自然耗尽,并且该 Output 已排空 |
failed | failed | 当前 Output 无法继续输出媒体 |
| 成员 | 适用对象 | 说明 |
|---|---|---|
state | Audio、Video | 当前 Output 状态 |
onStateChanged | Audio、Video | Output 状态变化通知 |
onError | Audio、Video | 当前 Output 的解码、播放或渲染错误 |
view(props) / TiCloudStorageVideoOutputView | Video | 创建由该 Output 驱动的 Fabric 视频组件;resizeMode 默认为 contain |
attach(replay, channelId) | Audio、Video | 绑定 Replay 中指定 channel_id 的媒体;channelId 取值为 0..255 |
takeSnapshot() | Video | 把当前视频画面保存为唯一的临时 JPEG;Promise 成功时返回 TiCloudStorageSnapshotFile |
detach() | Audio、Video | 解除当前 Replay 绑定;对象仍可再次 attach |
dispose() | Audio、Video | 释放 Output;调用后不能继续使用该实例 |
view(props) 是 TiCloudStorageVideoOutputView({ output: this, ...props }) 的惯用入口。Fabric 组件的 mount/unmount 只建立和解除宿主渲染面,不改变 Replay 绑定。
Snapshot 保存当前绑定和本次播放中最近一次成功呈现、且仍可读取的画面,不包含字幕、按钮等页面 UI。成功时 data.path 是 SDK 私有 cache 中的临时 JPEG。需要长期保存时调用文件对象的 moveToGallery(),直接使用完成后调用 delete()。当前绑定和本次播放尚未成功出画时返回 no-frame;暂停保留当前画面,自然播放完成后只要最终画面仍在就可以截图。重新 attach、开始新一次 play、detach、卸载 View 或清空画面后,必须等新画面成功呈现才能再次截图。
同一个 Video Output 同时只接受一个 Snapshot;已有请求未完成时,新 Promise resolve 为 in-use。不同 Output 仍可能因全局资源上限 resolve 为 resource-exhausted。Snapshot 完成前,dispose() 和 TiCloudStorage.shutdown() 返回 in-use,Output 及其绑定关系保持不变。
Video Output 和 Audio Output 分别使用设备配置提供的 channelId,取值范围是 0..255;两者可以相同,也可以不同。处理所有 union 分支时,需要包含 paused 和 completed。
重复绑定同一个 Replay 的同一个 channel_id 会成功;已经绑定时传入不同 Replay 或 channel_id 返回 in-use 并保留原关系,必须先显式 detach。Replay 正在运行时新增或 detach 后重新绑定 Output,从绑定生效后的下一可独立解码位置开始,不补发此前媒体。同一个 Replay 的同一个 channel_id 只能绑定一个同类播放 Output。stop、自然完成和新 play 都保留绑定;dispose 不隐式解绑,关系仍存在时返回 in-use。某个 Output 解码、播放或渲染失败时,它的 onError 返回错误,并且只终止该 Output。Token、网络、所选范围过大或录像读取失败时,Replay 的 onError 返回一次错误,并使本次回放的全部 Output 进入 failed。
本地媒体文件
export type TiCloudStorageGalleryAsset = Readonly<{ uri: string }>;
export interface TiCloudStorageRecordingFile {
readonly path: string;
readonly durationMs: number;
moveToGallery(fileName?: string): Promise<Resp<TiCloudStorageGalleryAsset>>;
delete(): Promise<number>;
}
export interface TiCloudStorageSnapshotFile {
readonly path: string;
moveToGallery(fileName?: string): Promise<Resp<TiCloudStorageGalleryAsset>>;
delete(): Promise<number>;
}两个文件对象的 path 在成功返回时指向 SDK 私有 cache。moveToGallery(fileName?) 的命名、权限和生命周期合同与 RTC 文件对象一致:省略名字时使用毫秒时间戳默认名,非法名字 resolve 为 6000;SDK 不申请图库权限,应用在调用前完成 Android 或 iOS 授权。
成功后返回系统媒体库 URI 并删除源文件,失败保留源文件并允许换名重试;delete() 受限、幂等。两项操作互斥;规范化后同名的并发 move 合并,不同名返回 in-use。成功后任何合法名字都返回第一次创建的 asset。
TiCloudStorageRecordingTask
export type TiCloudStorageStartRecordingOptions = Readonly<{
videoChannelId: number;
audioChannelId?: number;
}>;
export class TiCloudStorageRecordingTask {
stop(): Promise<Resp<TiCloudStorageRecordingFile>>;
}TiCloudStorageReplay.startRecording(options) 同步返回 Resp<TiCloudStorageRecordingTask>。Replay 必须正在回放;视频 Channel 必填,音频 Channel 可空。同步失败不创建 Task。
RecordingTask 和 ExportTask 的 MP4 格式范围见确认录像可以生成 MP4。一个 Task 期间,所选视频 Channel 的编码格式和分辨率必须保持不变;跨越格式变化点时,按格式稳定的时间范围分别创建 Task。
stop() 停止接收新媒体,排空已接收数据并完成 MP4。第一次调用开始终结;重复调用返回同一个 Promise。Replay 自然结束时 Task 自动完成,随后调用 stop() 返回缓存结果。
Task 不公开 state、实时 duration、cancel 或 dispose。相同 Channel 可以创建多个 Task,SDK 为每个 Task 生成不同文件;两个视频 Channel 使用两个 Task。
TiCloudStorageExportTask
export type TiCloudStorageExportOptions = Readonly<{
startTimeMs: number;
endTimeMs: number;
videoChannelId: number;
audioChannelId?: number;
}>;
export type TiCloudStorageExportProgress = Readonly<{
fraction: number;
coveredDurationMs: number;
}>;
export type TiCloudStorageExportObserver = Readonly<{
onProgress?: (progress: number) => void;
onProgressDetail?: (progress: TiCloudStorageExportProgress) => void;
onRecordingGap?: (gap: TiCloudStorageRecordingGap) => void;
}>;
export type TiCloudStorageExportReport = Readonly<{
requestedRange: TiCloudStorageRecordingRange;
coveredDurationMs: number;
segments: ReadonlyArray<TiCloudStorageExportSegment>;
gaps: ReadonlyArray<TiCloudStorageRecordingGap>;
unprocessedRanges: ReadonlyArray<TiCloudStorageRecordingRange>;
complete: boolean;
termination: TiCloudStorageExportTermination;
cause: number;
}>;
export type TiCloudStorageExportOutcome = Readonly<{
code: number;
file: TiCloudStorageRecordingFile | null;
report: TiCloudStorageExportReport | null;
}>;
export class TiCloudStorageExportTask {
readonly progress: number;
readonly progressDetail: TiCloudStorageExportProgress;
readonly report: TiCloudStorageExportReport | null;
readonly result: Promise<Resp<TiCloudStorageRecordingFile>>;
readonly completion: Promise<TiCloudStorageExportOutcome>;
cancel(): number;
stop(): Promise<Resp<TiCloudStorageRecordingFile>>;
}cloudStorage.exportRecording(options, observer) 在启动 Task 前固定实例当前的 Token,并保存可选 observer,然后同步返回 Resp<TiCloudStorageExportTask>。为兼容简单进度展示,第二个参数也可以直接传 (progress: number) => void。成功只表示 Task 已经开始。
同步失败不创建 Task,也不调用回调。回调不会在 exportRecording 返回前触发。
progress 位于 0.0..1.0 且单调不回退;progressDetail 还提供已覆盖的录像时长。中间通知可以合并,两个属性始终返回当前快照。普通导出继续等待 result 并判断是否取得文件即可。
只有业务需要判断导出是否完整、展示录像缺口或统计实际覆盖时长时,才需要等待 completion 并读取 report。完整或部分可播放的 MP4 都可能正常返回;complete 只在扫描结束、所选媒体没有确认缺口且没有未处理范围时为 true。gaps 是已经确认的来源缺口,unprocessedRanges 是任务提前结束时尚未处理的范围。
cancel() 发出非阻塞取消请求,之后仍需等待 completion 或 result 收取任务终局。取消与自然完成可能竞争,以最终结果为准。活动 Task 调用 stop() 会停止导出并清理未完成文件;自然完成或失败后调用 stop() 返回缓存的基本结果。重复调用返回同一个 Promise。
Task 在最终结果返回后自动释放底层执行资源,不提供 dispose。不同 Task 相互独立并生成不同 cache 文件;资源达到上限返回 resource-exhausted。
原始音视频诊断采集
Replay 可以按 Channel ID 采集用于问题排查的原始音视频数据。该能力不生成业务 MP4。
type TiCloudStorageRawDumpOptions = Readonly<{
audioChannelIds?: ReadonlyArray<number>;
videoChannelIds?: ReadonlyArray<number>;
}>;
type TiRawDumpStopReason =
| 'unknown'
| 'user'
| 'timeLimit'
| 'byteLimit'
| 'sourceClosed'
| 'resourceLimit'
| 'writeFailed';
type TiRawDumpArchive = Readonly<{
captureId: string;
path: string;
size: number;
sha256: string;
capturedDurationMs: number;
captureComplete: boolean;
stopReason: TiRawDumpStopReason;
unsavedPacketCount: number;
unsavedByteCount: number;
empty: boolean;
}>;
export class TiRawDump {
private constructor();
stop(): Promise<Resp<TiRawDumpArchive>>;
dispose(): Promise<number>;
}两个 Channel ID 数组至少填写一个,取值范围是 0..255。停止后检查返回的完整性和停止原因,再调用 TiRtcLogging.upload() 将采集文件随下一次日志上传提交。完整流程、限制和隐私要求见接入客户端诊断能力。
TiRtcLogging
TiRtcLogging 用于上传 SDK 日志。完成 Ti 云存初始化后调用;上传成功后,把返回的 logId 提供给支持人员。开发阶段的入口设计和接入流程见接入客户端诊断能力。
// 上传当前 SDK 日志;code == 0 且 logId != null 表示上传成功。
static upload(): Promise<TiRtcLoggingUploadResult>相关类型:
type TiRtcLoggingUploadResult = Readonly<{
code: number;
logId: string | null;
}>;示例:
import {TiRtcLogging} from 'tirtc-react-native';
const result = await TiRtcLogging.upload();
if (result.code === 0 && result.logId !== null && result.logId.length > 0) {
console.info(`Ti Cloud Storage logId=${result.logId}`);
}调用前必须完成 TiCloudStorage.init。code == 0 且 logId 非空表示上传已完成,可以继续调用 TiCloudStorage.shutdown()。Android 端如果因等待超时返回 code == 1001 且 logId == null,底层上传可能仍在收尾;不要把 Promise 已返回视为上传已结束。此时调用 TiCloudStorage.shutdown() 可能返回 in-use (6026),等待后重试关闭。其他非 0 错误码或空 logId 表示没有取得可提交的日志编号;按问题排查保留错误码和原始日志。
错误码
方法、结果对象和回调会返回完整整数错误码。常量列为“—”时没有对应的命名常量,错误码仍会原样返回。
| 错误码 | 常量 | 常见含义 | 建议处理 |
|---|---|---|---|
| 0 | TI_CLOUD_STORAGE_ERROR_OK | 操作成功 | 继续后续流程 |
| 6000 | TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT | 参数缺失、取值越界或时间范围无效 | 修正参数后重试 |
| 6001 | TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED | 尚未初始化 TiCloudStorage,或已经关闭 | 先调用 TiCloudStorage.init |
| 6014 | TI_CLOUD_STORAGE_ERROR_TOKEN_EXPIRED | Ti 云存 Token 已过期 | 为同一设备更新 Token 后重新发起请求 |
| 6022 | TI_CLOUD_STORAGE_ERROR_ALREADY_INITIALIZED | 已使用另一组配置初始化 | 保持初始化参数一致,或在全部资源释放后重新初始化 |
| 6024 | TI_CLOUD_STORAGE_ERROR_PERMISSION_DENIED | Token 无效,或无权访问目标设备或录像 | 检查授权并重新取得 Token |
| 6026 | TI_CLOUD_STORAGE_ERROR_IN_USE | 对象仍在播放、录制或执行任务,当前操作不能执行 | 先结束活动操作,再重试 |
| 6027 | TI_CLOUD_STORAGE_ERROR_NOT_STARTED | 尚未开始播放或录制 | 先启动对应操作 |
| 6029 | TI_CLOUD_STORAGE_ERROR_NOT_BOUND | 尚未建立当前操作要求的绑定 | 完成对应绑定后重试 |
| 6030 | TI_CLOUD_STORAGE_ERROR_NOT_CONFIGURED | 缺少必要的目标或媒体通道配置 | 补充通道或目标配置后重试 |
| 6043 | TI_CLOUD_STORAGE_ERROR_RESOURCE_EXHAUSTED | 当前设备没有足够资源创建或继续任务 | 结束其他任务并释放资源后重试 |
| 6044 | — | 初始化时无法创建或打开 SDK 工作目录 | 检查应用缓存目录和文件系统状态后重试 |
| 6045 | — | 移动到系统媒体库时无法读取源缓存文件,或源文件已经删除 | 确认文件对象仍有效,并检查缓存文件是否可读 |
| 6046 | TI_CLOUD_STORAGE_ERROR_FILE_WRITE_FAILED | 无法创建或写入输出文件 | 检查目录权限和磁盘空间 |
| 6113 | TI_CLOUD_STORAGE_ERROR_UNSUPPORTED_FORMAT | 当前操作遇到不支持的音视频格式;保存或导出任务内的视频编码格式或分辨率发生变化时也可能返回 | 先确认失败的是播放、保存还是导出;保存或导出时按格式稳定的时间范围拆分任务 |
| 6114 | TI_CLOUD_STORAGE_ERROR_IO_FAILED | 底层 I/O 或运行时边界失败 | 保留错误码并检查运行环境 |
| 6115 | TI_CLOUD_STORAGE_ERROR_CANCELLED | List 请求被取消 | 结束对应等待 |
| 6117 | TI_CLOUD_STORAGE_ERROR_RANGE_TOO_LARGE | List 查询跨度超过 10 天或合并结果超过 10000 项 | 缩短查询范围后重试 |
| 6118 | TI_CLOUD_STORAGE_ERROR_NO_FRAME | Video Output 尚无可用于截图的视频帧 | 等待出画后重试 |
| 6119 | TI_CLOUD_STORAGE_ERROR_NO_RECORDABLE_MEDIA | 所选范围或通道没有可写入的媒体 | 重新选择录像范围或通道 |
| 6120 | TI_CLOUD_STORAGE_ERROR_RECORDING_OVERRUN | 本地写入持续跟不上回放数据,保存任务已经终止 | 释放设备资源、检查存储性能后重新保存 |
| 6122 | TI_CLOUD_STORAGE_ERROR_RECORDING_UNREADABLE | 录像文件已经下载,但 SDK 无法解密或解析出有效的音视频帧,当前任务无法继续 | 不要持续重试同一文件;AAC 录像应确认每次写入都保留完整 ADTS 头,采样率和声道数与实际音频及 flags 一致,ADTS 声明的帧长度与本次写入字节数一致 |
| 6123 | TI_CLOUD_STORAGE_ERROR_UNAVAILABLE | SDK 无法完成录像查询或取得下载信息,通常是网络中断、Ti 云存服务暂时不可用或服务响应异常 | 稍后重新查询;持续失败时上报日志 |
| 6124 | TI_CLOUD_STORAGE_ERROR_STOPPED | 活动 ExportTask 已被主动停止 | 结束导出进度,不使用输出文件 |
| 6130 | — | 解码型 Output 持续收到同类媒体,但 5 秒内没有匹配绑定的 Channel ID | 核对设备上传与客户端绑定的 Channel ID,修正后重新播放 |
| 6134 | TI_CLOUD_STORAGE_ERROR_RECORDING_NOT_FOUND | 云端明确返回目标录像文件不存在;查询结果可能已过期,或文件已被删除 | 刷新录像列表后重新选择,不持续重试同一文件 |
| 6135 | TI_CLOUD_STORAGE_ERROR_RECORDING_DOWNLOAD_FAILED | SDK 未能成功下载目标录像文件;常见原因包括请求签名失败、连接超时、HTTP 错误、响应不完整或有限重试耗尽 | 稍后重试一次;持续失败时上报日志 |
| 6136 | TI_CLOUD_STORAGE_ERROR_NETWORK_UNAVAILABLE | 当前网络不可用,SDK 无法开始或继续访问录像服务 | 检查系统网络状态和网络权限;网络恢复后重新发起操作 |
| 6137 | TI_CLOUD_STORAGE_ERROR_ENDPOINT_DNS_RESOLUTION_FAILED | SDK 无法解析当前录像服务地址的域名 | 检查服务地址拼写和设备 DNS 配置;使用默认地址时上传日志并联系技术支持 |
有公开常量时优先使用常量判断错误类型;没有命名常量时直接比较返回的整数错误码。不要根据 errorToString 返回值或日志文本判断错误类型。
事件与生命周期
- 同一对象的事件按产生顺序串行投递;最终 Promise resolve 是该次运行的最后一次通知。
- JavaScript runtime 重新加载时,SDK 会停止该运行环境中的活动 Task、清理临时文件并释放句柄。旧运行环境已不存在,因此无法再完成其中创建的 Promise。显式退出页面时,仍应在运行环境存活期间结束 Task 并等待结果。
dispose是 checked 且可重试的:Replay 仍在播放、仍有 Output 绑定或活动 RecordingTask,或者 Output 仍绑定 Replay、仍被 Fabric View 承载或仍有活动 Snapshot 时返回in-use。失败时对象及关系保持不变。dispose只有在 native destroy 真正成功后才返回TI_CLOUD_STORAGE_ERROR_OK;重复调用幂等返回TI_CLOUD_STORAGE_ERROR_OK。成功后普通方法返回not-initialized,getter 保留最后提交值,对象不再接收普通事件。- MP4 Task 在最终结果返回后自动释放底层执行资源,不需要额外 dispose。
- Ti 云存和 Output 构造器只创建 JavaScript 对象,不申请底层资源,也不抛 SDK 运行错误;
createReplay同样只创建 Replay 对象。资源创建失败由第一次需要该资源的方法返回。