Skip to content

Flutter API 说明

Flutter 通过 tirtc_flutter 查询、回放、截图和导出云录像。

安装和初始化见 Flutter SDK 接入。查询、播放、下载和截图见播放录像

API 和公开错误常量都从这个 package 入口导出。需要业务分支处理的错误使用常量判断;完整数值见错误码TiStore.errorToString(code) 只用于展示或诊断。

dart
import 'package:tirtc_flutter/tirtc_flutter.dart';

最小回放示例

这段代码从查询录像开始,创建 Replay 并输出音视频。回放期间保持 storereplayvideoOutputaudioOutput 有效:

dart
final initCode = await TiStore.init(appId: appId);
if (initCode != kTiStoreErrorOk) return;

final store = TiStore(token: token);
final recordingsResp = await store.listRecordings(
  startTimeMs: startTimeMs,
  endTimeMs: endTimeMs,
);
if (!recordingsResp.success) {
  return;
}

final recordings = recordingsResp.data!;
if (recordings.isEmpty) return;

final recording = recordings.first;
final replay = store.createReplay();
final videoOutput = TiStoreVideoOutput();
final audioOutput = TiStoreAudioOutput();
replay.onError = showReplayError;
replay.onCompleted = showReplayCompleted;

videoOutput.attach(
  replay: replay,
  channelId: videoChannelId,
);
audioOutput.attach(
  replay: replay,
  channelId: audioChannelId,
);
replay.play(
  startTimeMs: recording.startTimeMs,
  endTimeMs: recording.endTimeMs,
);

attachplay 返回非 kTiStoreErrorOk 时,当前操作没有生效。回放过程中出现 Token、网络或录像读取错误时,replay.onError 返回对应错误码;请求范围自然播放结束时,replay.onCompleted 通知一次。Output 状态用于显示各路媒体的加载、播放、暂停和排空状态。

在播放页的 build 方法中使用 AspectRatio(child: videoOutput.view()) 显示画面。页面退出时先 stop Replay,再依次 detachdispose 两个 Output 和 Replay;不再访问这台设备时释放 store

基础类型

Resp<T>

dart
final class Resp<T> {
  // SDK 内部构造;不公开 constructor。
  final bool success;
  final int? code;
  final String? message;
  final T? data;
}
字段说明
success请求是否成功;为 true 时读取 data
code请求失败时的错误码;成功时为 null
message面向展示或诊断的错误说明;不要用它判断错误类型
data当前 API 的业务数据;请求失败时为 null

Resp<T> 由 SDK 创建,调用方只读取结果。success == true 时,data 一定非空,codemessagenullsuccess == false 时,datanullcode 非空。SDK 保证这组不变量,因此检查成功后可以直接读取 data!。SDK 运行错误通过返回值或 Resp<T> 报告,不抛 Dart exception。

TiStoreRecordingRange

dart
final class TiStoreRecordingRange {
  final int startTimeMs;
  final int endTimeMs;
}

表示一段存在云录像的可用时间。

字段说明
startTimeMs开始时间,UTC Unix 毫秒
endTimeMs结束时间,UTC Unix 毫秒;必须晚于 startTimeMs

TiStoreRecordingDay

dart
final class TiStoreRecordingDay {
  final String date;
  final bool hasRecording;
}
字段说明
date严格的 YYYY-MM-DD 日期
hasRecording该自然日至少有一个当前可见的录像时点时为 true

本地媒体文件

dart
final class TiStoreGalleryAsset {
  final Uri uri;
}

final class TiStoreRecordingFile {
  final String path;
  final Duration duration;

  Future<Resp<TiStoreGalleryAsset>> moveToGallery({String? fileName});
  Future<int> delete();
}

final class TiStoreSnapshotFile {
  final String path;

  Future<Resp<TiStoreGalleryAsset>> moveToGallery({String? fileName});
  Future<int> delete();
}

path 在成功返回时指向 SDK 私有 cache。TiStoreRecordingFile.duration 是 MP4 的实际媒体时长,不是 Task 从开始到结束经过的墙钟时间。需要长期保存时及时调用 moveToGallery() 或自行复制、移动;直接使用完成后调用 delete()

moveToGallery({fileName}) 可以为 MP4 或 JPEG 指定保存名称。省略名称时,SDK 使用 Unix 毫秒时间戳生成名称;省略扩展名时,SDK 按文件类型补上 .mp4.jpg。如果名称包含扩展名,只接受对应类型。名称主体按 UTF-8 计为 1~200 字节,不能为空、...,也不能包含 /\ 或空字符;非法名称返回 6000,不创建资产。

移动端写入系统媒体库,Windows 和 macOS 写入 Downloads 根目录。应用应在调用前准备所需权限,SDK 不主动申请图库权限。成功后返回不透明媒体资产 URI,并删除 cache 源文件;失败时保留源文件,可以换名重试。delete() 只清理 SDK 生成的临时 MP4/JPEG,且可以重复调用。移动与删除互斥;并发移动只有规范化后的同名请求会合并,不同名称返回 in-use。成功后 path 只保留历史值。

TiStoreReplaySpeed

dart
enum TiStoreReplaySpeed {
  x1,
  x2,
  x4,
  x8,
}
枚举值说明
x11 倍速,播放音频和视频
x22 倍速,视频播放,音频静音
x44 倍速,视频播放,音频静音
x88 倍速,视频播放,音频静音

TiStore

dart
final class TiStore {
  TiStore({required String token});

  static Future<int> init({
    required String appId,
    String endpoint = '',
    bool consoleLogEnabled = false,
  });

  static String errorToString(int code);

  void Function()? onTokenExpired;

  int updateToken(String token);

  Future<Resp<List<TiStoreRecordingDay>>> listRecordingDays({
    required String startDate,
    required String endDate,
    String timeZoneId = 'Asia/Shanghai',
  });

  Future<Resp<List<TiStoreRecordingRange>>> listRecordings({
    required int startTimeMs,
    required int endTimeMs,
  });

  TiStoreReplay createReplay();

  Resp<TiStoreExportTask> exportRecording({
    required int startTimeMs,
    required int endTimeMs,
    required int videoChannelId,
    int? audioChannelId,
    void Function(double progress)? onProgress,
  });

  int dispose();
}

appId 必须非空,使用申请开通下发的 AppId,并与签发 Token 时使用的值一致。consoleLogEnabled 默认关闭。

使用任何云存储 API 前先调用 TiStore.init。相同配置重复初始化会成功;改用不同配置则返回 already-initialized。初始化后用一台设备的非空 app_access_token 创建 TiStore 实例;SDK 把 Token 作为不透明字符串使用,创建实例时不发起网络请求。查询、Replay 和导出都访问这个 Token 所绑定的设备;同时访问多台设备时,为每台设备创建独立实例。Flutter package 自行管理日志和缓存目录。

API说明
init(appId, endpoint, consoleLogEnabled)初始化。appId 来自申请开通endpoint 留空时使用默认服务地址;consoleLogEnabled 默认为 false。Future 返回初始化错误码
errorToString(code)返回错误码对应的稳定名称,用于日志和诊断
updateToken(token)为同一设备更新 Token;只影响之后启动的 List、Play 和 Export
listRecordingDays(startDate, endDate, timeZoneId)查询包含式日期范围,单次最多 31 天。时区使用 IANA ID,默认 Asia/Shanghai;Future 返回完整的逐日升序结果
listRecordings(startTimeMs, endTimeMs)查询当前设备在 [startTimeMs, endTimeMs) 内的录像可用时间段。Future 返回 Resp<List<TiStoreRecordingRange>>
createReplay()创建属于当前设备的 Replay
exportRecording(startTimeMs, endTimeMs, videoChannelId, audioChannelId, onProgress)开始一次录像导出。视频 Channel 必填,音频 Channel 和进度回调可选。同步返回 Resp<TiStoreExportTask>
dispose()释放当前设备实例;仍有查询、Replay、ExportTask 或回调正在使用时返回 in-use

onTokenExpired 在当前 Token 被云端明确判定为过期后通知,供应用统一刷新凭据。触发过期的 List、Play 或 Export 会先通过自己的结果报告错误;无效 Token 和权限不足不触发该回调。同一个 Token 至多通知一次,晚设置回调不补发历史事件。

updateToken 的非空新值在返回前被 SDK 保存,之后启动的操作使用这个 Token。传入相同 Token 是幂等操作,不会清除已经确定的过期状态。当前 Token 已经确定过期时,新 List、Play 和 Export 直接返回共享错误 token-expired,不创建操作。updateToken 只用于同一设备续签,访问另一台设备时创建新的 TiStore

listRecordingDays

dart
Future<Resp<List<TiStoreRecordingDay>>> listRecordingDays({
  required String startDate,
  required String endDate,
  String timeZoneId = 'Asia/Shanghai',
});

startDateendDate 使用严格的 YYYY-MM-DD 格式,起止日期均包含。timeZoneId 使用 IANA 时区 ID;不要传 UTC+8Local。单次最多查询 31 天,超过上限时返回 range-too-large

成功结果按日期升序返回范围内每一天,包括 hasRecording == false 的日期。hasRecording == true 表示该自然日至少有一个当前可见的录像时点;要取得可回放时间段,再调用 listRecordings

listRecordings

dart
Future<Resp<List<TiStoreRecordingRange>>> listRecordings({
  required int startTimeMs,
  required int endTimeMs,
});

查询设备在指定 UTC 时间范围内的云录像。startTimeMsendTimeMs 使用 UTC Unix 毫秒,SDK 不根据本地时区换算;startTimeMs 必须早于 endTimeMs。单次查询范围最长为 10 天,最多返回 10000 段;没有录像时,成功结果的 data 为空列表。

结果按 startTimeMs 升序排列并裁剪到 [startTimeMs, endTimeMs)。重叠或首尾相接的时间段会合并,真实空洞会保留;超过范围或结果数量上限时,本次查询整体失败,不返回部分结果。

exportRecording

dart
Resp<TiStoreExportTask> exportRecording({
  required int startTimeMs,
  required int endTimeMs,
  required int videoChannelId,
  int? audioChannelId,
  void Function(double progress)? onProgress,
});

同步检查参数和本地资源。成功时 data 是已经开始运行的独立 ExportTask;失败时不创建 Task,也不留下临时文件。Runtime 在初始化时确定的私有 cache 中为每个 Task 生成唯一 MP4,调用方不传输出路径。

需要进度通知时传入 onProgress。SDK 在启动 Task 前保存回调,并且不会在 exportRecording 返回前调用它。

  • startTimeMsendTimeMs 使用 UTC Unix 毫秒,范围为 [startTimeMs, endTimeMs),并且 startTimeMs 必须早于 endTimeMs
  • videoChannelId 必填,audioChannelId 可空,值必须位于 0..255;两者可以相同;
  • 录像中没有媒体的时间会被跳过,不写黑帧或静音;
  • 多次调用彼此独立并生成不同文件;超过并发资源上限返回 resource-exhausted

输入格式、转码和关键帧规则见播放录像

TiStoreReplay

dart
final class TiStoreReplay {
  TiStoreReplay._();

  TiStoreReplaySpeed get speed;
  int? get currentTimeMs;
  void Function(int timeMs)? onTimeChanged;
  void Function()? onCompleted;
  void Function(int code)? onError;

  int play({
    required int startTimeMs,
    required int endTimeMs,
    int? initialTimeMs,
  });

  int pause();
  int resume();

  int seek(int timeMs);

  int setSpeed(TiStoreReplaySpeed speed);

  Resp<TiStoreRecordingTask> startRecording({
    required int videoChannelId,
    int? audioChannelId,
  });

  int stop();
  int dispose();
}

Replay 只能由 store.createReplay() 创建,并固定属于该设备实例。它提供播放、暂停、恢复、Seek、倍速和停止操作。对应 Output 返回音频和视频的缓冲、输出、暂停、完成或失败状态。Token、网络或录像读取错误通过 Replay 返回。

属性和时间回调

  • speed:当前速度,初始值为 TiStoreReplaySpeed.x1
  • currentTimeMs:当前已知的绝对 UTC 回放时间;尚未开始、尚未获得首个时间位置或已经停止时为 null。暂停时保留最后位置,自然完成时为请求的 endTimeMs,回放失败时保留最后已知位置;
  • onTimeChanged:回放时间变化通知。中间通知可以合并,应用不能用通知次数计算时长。
  • onCompleted:当前 play 请求的时间范围自然耗尽时恰好调用一次。主动 stop、被新的 play 替换、最后一个消费者解除绑定或回放失败都不会触发;它表示 Replay 已经自然结束,不表示各 Output 已经排空;
  • onError:返回当前 play 过程中发生的 Token、权限、网络或录像读取错误。错误发生时恰好调用一次;同步拒绝、主动 stop、被新 play 替换或自然完成不会触发。

晚设置回调不会重放此前事件。开始 play 前设置 onCompletedonError;需要立即显示当前位置时,先读取 currentTimeMs,再设置 onTimeChanged。范围自然耗尽时,Replay 会自动终结活动 RecordingTask;应用稍后调用 Task 的 stop() 取得缓存结果。

play

dart
int play({
  required int startTimeMs,
  required int endTimeMs,
  int? initialTimeMs,
});

开始回放所属设备的指定 UTC 时间范围。initialTimeMs 省略时从 startTimeMs 开始;传入时从指定位置开始。Replay 必须已经绑定至少一个 Audio/Video Output。方法接受请求时固定使用实例当前的 Token;之后调用 updateToken 不改变本次 Play。

返回 kTiStoreErrorOk 表示请求已经接受,不表示媒体已经开始输出。通过 Output 状态更新播放界面;Token、网络或录像读取失败时,在 Replay onError 中处理。

startTimeMs 必须早于 endTimeMsinitialTimeMs 必须位于 [startTimeMs, endTimeMs)。参数无效时同步返回 invalid-argument,当前回放不受影响。请求被接受后直接从初始位置建立回放,不会先输出范围开头的媒体。初始位置落在录像空洞中时,从其后的第一个可播放位置开始;直到范围末尾都没有录像时,按自然结束处理。

同一个 Replay 可以再次调用 play

  • 新请求被接受后,前一个回放停止;
  • 已绑定的 Output、Audio Output 音量和当前速度保留;
  • 新回放从非暂停状态开始;
  • 新请求被同步拒绝时,原来的回放不受影响。

pause / resume

dart
int pause();
int resume();

控制当前 Replay 的暂停与继续。

  • 暂停成功后,已绑定 Output 进入 paused
  • 恢复后,Output 根据当前数据进入 bufferingplayingrendering
  • 已经暂停时再次 pause、已经继续时再次 resume,都会成功且不产生额外任务。
  • 尚未 play、已经 stop、播放完成或回放失败时返回 not-started

seek

dart
int seek(int timeMs);

通知 Replay 跳转到当前回放范围内的绝对 UTC 时间。返回 kTiStoreErrorOk 表示定位命令已经接受,不表示媒体已经到达目标位置。

  • timeMs 必须位于当前 [startTimeMs, endTimeMs);越界时同步返回 invalid-argument,当前回放不受影响;
  • Seek 保留当前速度和暂停意图,连续调用采用 latest-wins;
  • 定位生效后,currentTimeMsonTimeChanged 更新为第一个不早于 timeMs 的可播放位置;
  • 目标合法但其后直到范围末尾都没有录像时,位置推进到 endTimeMs,并按自然结束触发 onCompleted

Seek 只能在当前 play 仍在运行或暂停时调用。尚未 play、已经 stop、自然完成或回放失败时返回 not-started。自然完成后要从指定位置重新播放时,调用带 initialTimeMsplay

setSpeed

dart
int setSpeed(TiStoreReplaySpeed speed);

设置当前和后续 play 使用的速度。设置相同速度是幂等操作。

startRecording

dart
Resp<TiStoreRecordingTask> startRecording({
  required int videoChannelId,
  int? audioChannelId,
});

在当前回放中开始保存一段 MP4。Replay 必须正在运行。videoChannelId 必填,audioChannelId 可空;两者都必须位于 0..255

成功只表示 Task 已经建立并开始接收媒体。Replay 未活动或资源达到上限时,同步返回失败且 datanull。同一个 Channel 可以启动多个 Task,SDK 为每个 Task 生成不同文件。两个视频 Channel 使用两个 Task。

stop

dart
int stop();

停止当前回放并清除 currentTimeMs。已绑定 Output 回到 idle。尚未开始、已经完成或已经失败时调用 stop 也会成功。

stop 不产生 completed,并且保留 Output 绑定和速度。之后再次调用 play 不需要重新 attach。

dispose

dart
int dispose();

永久释放 Replay。调用前必须先 stop,结束全部 RecordingTask,并显式 detach 全部 Output;否则返回 in-use,对象保持有效,可完成清理后重试。成功后不再投递 Replay 回调,且对象不能再次使用。

Audio/Video Output

云录像任务使用以下 Output 成员。

dart
final class TiStoreAudioOutput {
  TiStoreAudioOutput();

  TiStoreAudioOutputState get state;
  void Function(TiStoreAudioOutputState state)? onStateChanged;
  void Function(int code)? onError;

  int attach({required TiStoreReplay replay, required int channelId});
  int setVolume(int volumePercent);
  int detach();
  int dispose();
}

final class TiStoreVideoOutput {
  TiStoreVideoOutput();

  TiStoreVideoOutputState get state;
  Size? get renderSize;
  void Function(TiStoreVideoOutputState state)? onStateChanged;
  void Function(Size size)? onRenderSizeChanged;
  void Function(int code)? onError;

  int attach({required TiStoreReplay replay, required int channelId});
  Future<Resp<TiStoreSnapshotFile>> takeSnapshot();
  int detach();
  Widget view();
  int dispose();
}

属性与回调

成员适用对象说明
stateAudio、Video当前 Output 状态
renderSizeVideo最近一次视频画面尺寸;尚未出画时为 null
onStateChangedAudio、VideoOutput 状态变化通知
onRenderSizeChangedVideo视频画面尺寸变化通知
onErrorAudio、Video当前 Output 的解码、播放或渲染错误;不会报告 Token、网络或录像读取错误

方法

API适用对象说明
attach(replay, channelId)Audio、Video绑定 Replay 中指定 channel_id 的音频或视频;channelId 取值为 0..255
setVolume(volumePercent)Audio设置当前 Output 音量,取值 0..1000 表示静音,不修改系统全局音量
takeSnapshot()Video把当前视频画面保存为唯一的临时 JPEG;Future 成功时通过 data 返回 TiStoreSnapshotFile
detach()Audio、Video解除当前 Replay 绑定;对象仍可再次 attach
view()Video返回该 Output 的渲染 Widget;同一个 Output 同时只能挂载一个 view()
dispose()Audio、Video释放已解除绑定的 Output;返回错误码,成功后不能继续使用该实例

Output 状态

dart
enum TiStoreAudioOutputState {
  idle,
  buffering,
  playing,
  failed,
  paused,
  completed,
}

enum TiStoreVideoOutputState {
  idle,
  buffering,
  rendering,
  failed,
  paused,
  completed,
}
状态Replay 场景中的含义
idle没有活动回放、已经 stop 或已经解除绑定
buffering回放活动,但当前没有足够媒体
playing / rendering正在输出音频或视频
pausedReplay 已暂停
completed目标时间范围结束,当前 Output 已排空
failed当前 Output 已无法继续;可能是回放失败,也可能是该 Output 自己失败

completed 只表示范围耗尽,不证明这个 Output 曾经输出媒体。所选 channel_id 在范围内没有匹配帧时也会完成;需要区分时,记录本次是否曾进入 playingrendering

某个 Output 解码、播放或渲染失败时,它通过自己的 onError 返回错误,其他 Output 可以继续工作。Token、网络、所选范围过大或录像读取失败时,Replay 的 onError 返回一次错误,相关 Output 进入 failed。修正问题后再次调用 play,有效的 Output 会从 buffering 重新开始。

绑定规则

  • attach 绑定 Replay 和对应媒体的 channelId
  • 重复绑定同一个 Replay 和 channel_id 会成功;
  • 已有不同 Replay 或 channel_id 的绑定时返回 in-use 并保留原绑定;换绑前必须先 detach
  • Replay 正在运行时新增 Output,从绑定生效后的下一可独立解码位置开始,不补发此前媒体;
  • 同一个 Replay 的同一个 channel_id 同时只能绑定一个同类播放 Output;
  • stopcompleted 和新的 play 都保留绑定;
  • Output 只有 detach 会解除 Replay 绑定;dispose 不隐式解绑;
  • 最后一个 Output 离开且没有活动 RecordingTask 时,当前回放自动停止,但不触发 onCompleted
  • RecordingTask 与播放 Output 相互独立;释放一个 Output 不会停止其他 Output 或活动 RecordingTask。

Snapshot

dart
Future<Resp<TiStoreSnapshotFile>> takeSnapshot();

保存 Video Output 当前仍可取得的最近画面。SDK 在可写缓存目录生成唯一的 JPEG。

  • 成功时 data.path 是 SDK 私有 cache 中的临时 JPEG,失败时 code 给出错误;
  • 需要长期保存时调用返回文件对象的 moveToGallery(),直接使用后调用 delete()
  • 当前绑定还没有画面时返回 no-frame;暂停或自然播放完成后,只要最后画面仍然保留,就可以继续截图;
  • attach 新来源、新的 play 生效、detach 或移除渲染 View 后,在新画面到达前返回 no-frame
  • Snapshot 不捕获 Flutter Widget、字幕、按钮或其他页面叠加内容;
  • 同一个 Video Output 同时只接受一个 Snapshot;已有请求未完成时,新的 Future 以 in-use 完成。不同 Output 的请求仍可能因全局资源上限以 resource-exhausted 完成;
  • Snapshot Future 完成前,dispose() 返回 in-use,对象与原有绑定保持不变。先等待 Future,再按正常顺序解除绑定并释放 Output。

TiStoreRecordingTask

dart
final class TiStoreRecordingTask {
  TiStoreRecordingTask._();

  Future<Resp<TiStoreRecordingFile>> stop();
}

RecordingTask 只能由活动 Replay 的 startRecording 创建。Replay、视频 Channel 和可选音频 Channel 在创建时一次确定;输出路径由 SDK 在私有 cache 中生成。

stop() 停止接收新媒体,排空已经接收的数据,完成 MP4 并返回文件结果。第一次调用开始终结;重复调用返回同一个 Future 和同一份结果。

  • 成功时 data.path 是临时 MP4 路径,data.duration 是文件实际媒体时长;
  • 还没有形成可写媒体时返回 no-recordable-media
  • codec、磁盘、媒体处理或来源失败时返回对应错误,且不保留半成品;
  • Replay 自然结束时 Task 自动完成;随后调用 stop() 取得已经缓存的结果;
  • 停止一个 RecordingTask 不影响 Replay 和其他 Task。

Task 没有 state、实时 duration、cancel、result 属性或 dispose。页面显示录制秒数时,由应用从开始动作自行计时;最终准确时长以 TiStoreRecordingFile.duration 为准。

TiStoreExportTask

dart
final class TiStoreExportTask {
  TiStoreExportTask._();

  double get progress;

  Future<Resp<TiStoreRecordingFile>> get result;

  Future<Resp<TiStoreRecordingFile>> stop();
}

TiStoreExportTask 只能由设备实例的 exportRecording 创建。它与 Replay 和 Output 相互独立,并在启动时固定使用实例当前的 Token。

成员说明
progress当前导出进度,取值为 0.0..1.0
result完整处理指定范围后的最终文件;每次读取返回同一个 Future
stop()停止活动导出并等待清理;返回同一个最终结果 Future

progress

  • 进度范围是 0.0..1.0,并且不会回退;
  • 跨越录像空洞时,进度可能直接跃迁;
  • 只有文件完整写入并关闭后,进度才会到达 1.0
  • exportRecording 传入的进度通知可以合并,progress 始终返回当前快照。

Export result

dart
Future<Resp<TiStoreRecordingFile>> get result;

同一个 Task 始终返回同一个 Future,并且只完成一次。

  • 文件完整写入并关闭后才返回成功;
  • 成功结果包含临时文件 path 和实际媒体 duration
  • 失败或主动停止会删除临时文件;
  • result 完成后自动释放任务使用的底层资源,不提供 dispose

stop

dart
Future<Resp<TiStoreRecordingFile>> stop();

活动任务调用 stop() 会停止读取并删除未完成文件,以 stopped 返回;它不会把已处理部分当作成功文件。任务已经自然完成或失败时,stop() 返回缓存的同一结果。重复调用返回同一个 Future。

生命周期与回调

并发规则

  • 不同 RecordingTask、ExportTask 和不同 Video Output 的 Snapshot 是独立操作,可以同时运行;
  • 每个 Task 生成一个 MP4,同一个 Video Output 同时只接受一个 Snapshot;
  • 相同 Channel 可以创建多个 Task,SDK 为每个 Task 生成不同文件;两个视频 Channel 使用两个 Task;
  • SDK 对并发网络、媒体处理和文件任务设置全局资源上限;超过上限的请求返回 resource-exhausted,不会建立无界队列;

回调与关闭

  • TiStore 实例绑定一台设备并提供查询、Replay 和导出;Output、RecordingTask 和 ExportTask 是具体资源;
  • 回调和 Future completion 返回创建对象的 Dart isolate,通常就是应用的 UI isolate;
  • 状态、时间和进度回调表示当前事实,中间变化可以合并;
  • Replay 和 Output 的状态或时间回调晚设置时不会重放此前事件,需要立即显示时先读取对象当前属性;Export 的进度回调在 exportRecording 中注册;
  • 最终 result 不会被合并或丢失;
  • 对象 dispose 或 Task 完成后,不再投递对应状态事件;已经接受的一次性 Future 仍会恰好完成;
  • Store 和 Output 构造器只创建对应对象,不抛 SDK 运行错误;createReplay 同样只创建 Replay。第一次需要底层资源的方法返回创建错误;
  • 页面退出时先结束活动 Task 并等待已接受的 Snapshot,再停止 Replay、释放 Output 和 Replay;
  • 不再访问对应设备时释放 Store 实例。

错误码

需要业务分支处理的错误在 package 根入口提供公开常量。方法仍返回完整整数错误码;业务判断使用常量,不解析错误消息。

错误码Flutter 常量常见含义建议处理
0kTiStoreErrorOk操作成功继续后续流程
6000kTiStoreErrorInvalidArgument参数缺失、取值越界或时间范围无效修正参数后重试
6001kTiStoreErrorNotInitialized尚未初始化云存储能力,或已经关闭先调用 TiStore.init
6014kTiStoreErrorTokenExpiredToken 已过期为同一设备更新 Token 后重新发起请求
6022kTiStoreErrorAlreadyInitialized已使用另一组配置初始化保持初始化参数一致;需要更换配置时,重启应用后再初始化
6024kTiStoreErrorPermissionDeniedToken 无效、无权访问目标录像,或应用尚未取得系统相册写入权限检查对应授权;云端访问失败时重新取得 Token
6026kTiStoreErrorInUse对象仍在播放、录制或执行任务,当前操作不能执行先结束活动操作,再重试
6027kTiStoreErrorNotStarted尚未开始播放或录制先启动对应操作
6029kTiStoreErrorNotBound对象尚未绑定 Replay完成 attach 后重试
6030kTiStoreErrorNotConfigured尚未选择媒体通道或完成必要配置补充通道或配置后重试
6043kTiStoreErrorResourceExhausted当前设备没有足够资源创建或继续任务结束其他任务并释放资源后重试
6046kTiStoreErrorFileWriteFailed无法创建或写入输出文件检查目录权限和磁盘空间
6113kTiStoreErrorUnsupportedFormat录像的音视频格式不受支持提示当前录像无法播放或导出
6115kTiStoreErrorCancelledList 请求被取消结束对应等待
6117kTiStoreErrorRangeTooLarge日期查询超过 31 天,或时间段查询超过 10 天或合并结果超过 10000 项缩短查询范围后重试
6118kTiStoreErrorNoFrameVideo Output 尚无可用于截图的视频帧等待出画后重试
6119kTiStoreErrorNoRecordableMedia所选范围或通道没有可写入的媒体重新选择录像范围或通道
6120kTiStoreErrorRecordingOverrun本地写入持续跟不上回放数据,保存任务已经终止释放设备资源、检查存储性能后重新保存
6122kTiStoreErrorRecordingUnreadable录像文件缺失、损坏,或上传的格式与帧数据无法读取文件缺失或损坏时停止重试;其他情况检查设备上传的格式与帧要求
6123kTiStoreErrorUnavailable网络、云存储服务或录像数据读取暂时不可用稍后重试
6124kTiStoreErrorStopped活动 ExportTask 已被主动停止结束导出进度,不使用输出文件

使用错误常量判断错误类型。TiStore.errorToString(code) 和错误消息只用于展示或诊断。

TiStore 开发文档