Skip to content

Ti 云存 Flutter API 参考

本文列出 Flutter 中 Ti 云存和通用 Media 的公开 API、返回结果和行为约定。第一次接入时,先阅读 实现云录像的回放与导出;需要确认参数、状态、重复调用或资源释放规则时,再查阅本文。

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

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

一、API 总览

类型用途
TiCloudStorage静态方法初始化和关闭服务;实例绑定一台设备并提供查询、Replay 和导出
Resp<T>表示一次云端业务请求的结果;成功时通过 data 返回业务数据
TiCloudStorageRecordingRange表示一段可回放的录像
TiCloudStorageReplay回放云录像、提供播放控制并返回回放错误
TiCloudStorageAudioOutput播放 Replay 的一路音频
TiCloudStorageVideoOutput显示 Replay 的一路视频并保存当前画面
TiCloudStorageRecordingTask保存 Replay 正在播放的一段内容
TiCloudStorageExportTask把指定时间范围内的云录像直接导出到本地
TiCloudStorageRecordingFileSDK cache 中的临时 MP4、实际媒体时长与后续文件操作
TiCloudStorageSnapshotFileSDK cache 中的临时 JPEG 与后续文件操作

常规的“列出录像并开始音视频回放”流程如下。示例只展示正常调用顺序;回放期间保持 replayvideoOutputaudioOutput 有效:

dart
final initCode = await TiCloudStorage.init(appId: appId);
if (initCode != kTiCloudStorageErrorOk) return;

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

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

final recording = recordings.first;
final replay = cloudStorage.createReplay();
final videoOutput = TiCloudStorageVideoOutput();
final audioOutput = TiCloudStorageAudioOutput();
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 返回非 kTiCloudStorageErrorOk 时,当前操作没有生效。回放过程中出现 Token、网络或录像读取错误时,replay.onError 返回对应错误码;请求范围自然播放结束时,replay.onCompleted 通知一次。Output 状态用于显示各路媒体的加载、播放、暂停和排空状态。

在播放页的 build 方法中使用 AspectRatio(child: videoOutput.view()) 显示画面,完整代码见 实现云录像的回放与导出。页面退出时,先 stop Replay,再依次 detachdispose 两个 Output 和 Replay。不再访问这台设备时释放 cloudStorage;应用不再使用 Ti 云存时,最后调用 TiCloudStorage.shutdown()

二、共同约定

时间

  • startTimeMsendTimeMstimeMsactualTimeMs 都是 UTC Unix 毫秒时间戳;
  • startTimeMs 必须小于 endTimeMs
  • 时间戳直接使用 Dart int;普通 Unix 毫秒值都在支持范围内;
  • SDK 不根据本地时区对时间戳做隐式换算。

channel_id

  • Ti 云存使用设备写入云录像的 channel_id,有效范围是 0..255
  • 视频和音频分别使用设备配置或业务服务提供的 Channel ID,两者可以相同,也可以不同;
  • Flutter Ti 云存 API 使用 channelId 作为参数名。Output 已经确定媒体类型,因此 attach 只选择一路对应媒体。每个 MP4 Task 必须选择一个视频通道,并且可以再选择一个音频通道。

Token、设备与录像范围

  • APP Access Token 由应用服务端为一台目标设备申请,并且只授权该设备;
  • SDK 把 Token 作为不透明字符串使用,不解析其内容;
  • 使用 Token 创建一个 TiCloudStorage 实例;查询、Replay 和导出都属于这台设备;
  • 同时访问多台设备时,每台设备创建独立实例,不用 updateToken 把一个实例切换到另一台设备;
  • List、Play 和 Export 在启动时固定使用当时的 Token。updateToken 只影响之后启动的操作,不迁移、恢复或重试已经开始的操作;
  • 当前 Token 被云端明确判定为过期时,触发操作直接返回共享错误 token-expired。应用取得新 Token、调用 updateToken 后,自行重新发起失败的操作;SDK 不另发过期通知。

错误和异步结果

  • 同步操作返回整数错误码;
  • 异步操作通过 Future 返回一次最终结果;
  • 云端业务查询使用 Resp<T>success 表示请求是否成功,data 是当前 API 返回的业务数据;
  • SDK 运行错误不抛 Dart exception;
  • startRecordingexportRecording 同步返回 Resp<Task>;成功只表示 Task 已经建立;
  • RecordingTask 的终局通过 stop() 返回,ExportTask 的自然终局通过 result 返回;两者成功时都交付 TiCloudStorageRecordingFile

三、基础类型

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!

TiCloudStorageRecordingRange

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

TiCloudStorageRecordingDay

dart
final class TiCloudStorageRecordingDay {
  const TiCloudStorageRecordingDay({
    required this.date,
    required this.hasRecording,
  });

  final String date;
  final bool hasRecording;
}

date 是严格 YYYY-MM-DD 日期。hasRecordingtrue 只表示该自然日至少有一个当前可见的录像时点。

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

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

本地媒体文件

dart
final class TiCloudStorageGalleryAsset {
  final Uri uri;
}

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

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

final class TiCloudStorageSnapshotFile {
  final String path;

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

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

moveToGallery({fileName}) 的平台目标、文件名规则和权限职责与 RTC 文件对象一致。移动端写入系统媒体库;Windows 和 macOS 写入 Downloads 根目录。省略名字时使用毫秒时间戳默认名;非法名字返回 6000,不创建资产。

成功后返回不透明媒体资产 URI,并删除 cache 源文件;失败时保留源文件,可换名重试。delete() 只清理 SDK 生成的临时 MP4/JPEG,且可以重复调用。两项操作互斥;规范化后同名的并发 move 合并,不同名返回 in-use。成功后任何合法名字都返回第一次创建的 asset,path 只保留历史值。SDK 不申请图库权限,应用应在调用前完成 Android 或 iOS 授权;OHOS 的系统资产创建确认只由用户保存动作触发。

TiCloudStorageReplaySpeed

dart
enum TiCloudStorageReplaySpeed {
  x0_125,
  x0_25,
  x0_5,
  x1,
  x2,
  x4,
  x8,
}
枚举值说明
x0_1251/8 倍速,视频慢放,音频静音
x0_251/4 倍速,视频慢放,音频静音
x0_51/2 倍速,视频慢放,音频静音
x11 倍速,播放音频和视频
x22 倍速,视频播放,音频静音
x44 倍速,视频播放,音频静音
x88 倍速,视频播放,音频静音

四、TiCloudStorage

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

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

  static int shutdown();

  static String errorToString(int code);

  int updateToken(String token);

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

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

  TiCloudStorageReplay createReplay();

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

  int dispose();
}

appId 必须非空,使用控制台分配的 AppId,并与应用服务端申请 Token 时使用的 AppId 一致。endpoint 只在专有环境或测试环境中设置;它不是对象存储地址。consoleLogEnabled 默认关闭。

使用任何 Ti 云存 API 前先调用 TiCloudStorage.init。使用相同配置重复初始化会成功,配置不同则返回 already-initialized。初始化后用一台设备的非空 Token 创建 TiCloudStorage 实例;创建实例不发起网络请求。同时访问多台设备时创建多个实例。释放全部 TiCloudStorage 实例、Replay、Output 和任务后调用 TiCloudStorage.shutdown

TiRTC 与 Ti 云存可以使用不同的 App ID 和 endpoint。Flutter package 从平台 cache 目录解析 tirtc 工作根目录,并将同一路径用于 TiRTC 与 TiCloudStorage;两者同时运行时,consoleLogEnabled 配置也必须一致。Runtime 在该目录下管理日志、日志归档和临时媒体文件。调用 TiCloudStorage.shutdown() 不会停止 TiRTC。

TiCloudStorage.shutdown() 在尚未初始化或已经关闭时幂等返回成功;仍有活动资源时返回 in-use。在对象回调中同步停止、解绑或释放当前对象及其关联资源,也会返回 in-use 且不改变状态。其他无关实例和资源不受此限制。

cloudStorage.dispose() 只有在底层资源释放成功后才使实例失效。若返回 in-use 或其他错误,实例和当前 Token 仍然有效;清理占用资源后可以重试。SDK 不负责清除调用方持有的 Token 字符串。

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

Token 过期由触发它的 List、Play 或 Export 直接报告 token-expired。SDK 不另发过期通知,也不自动刷新或重试。updateToken 的非空新值在返回前被 SDK 保存。传入相同 Token 是幂等操作,不会清除已经确定的过期状态;不同 Token 建立新的授权代际,只影响之后启动的操作。updateToken 只用于同一设备续签,访问另一台设备时创建新的 TiCloudStorage

listRecordingDays

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

startDateendDate 必须是真实存在的 YYYY-MM-DD 日期,起止都包含在结果中。日期倒置、超过 31 天、空时区或云端无法识别的 IANA 时区会使整次查询失败。成功结果精确包含范围内的每一天,包括 hasRecording=false 的日期。

listRecordings

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

查询设备在指定 UTC 时间范围内的云录像。单次查询范围最长为 10 天,最多返回 10000 段;没有录像时,成功结果的 data 为空列表。

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

exportRecording

dart
Resp<TiCloudStorageExportTask> 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 返回前调用它。

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

输入格式、转码和关键帧规则见 Ti 云存 API 总览

五、TiCloudStorageReplay

dart
final class TiCloudStorageReplay {
  TiCloudStorageReplay._();

  TiCloudStorageReplaySpeed 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(TiCloudStorageReplaySpeed speed);

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

  int stop();
  int dispose();
}

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

属性和时间回调

  • speed:当前速度,初始值为 TiCloudStorageReplaySpeed.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,
});

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

返回 kTiCloudStorageErrorOk 表示请求已经接受,不表示媒体已经开始输出。通过 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 时间。返回 kTiCloudStorageErrorOk 表示定位命令已经接受,不表示媒体已经到达目标位置。

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

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

setSpeed

dart
int setSpeed(TiCloudStorageReplaySpeed speed);

设置当前和后续 play 使用的固定速度,可选 1/8x1/4x1/2x1x2x4x8x。非 1x 时 Audio Output 静音;慢放画面采用持帧显示。设置相同速度是幂等操作。

startRecording

dart
Resp<TiCloudStorageRecordingTask> 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 TiCloudStorageAudioOutput {
  TiCloudStorageAudioOutput();

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

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

final class TiCloudStorageVideoOutput {
  TiCloudStorageVideoOutput();

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

  int attach({required TiCloudStorageReplay replay, required int channelId});
  Future<Resp<TiCloudStorageSnapshotFile>> 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 返回 TiCloudStorageSnapshotFile
detach()Audio、Video解除当前 Replay 绑定;对象仍可再次 attach
view()Video返回该 Output 的渲染 Widget;同一个 Output 同时只能挂载一个 view()
dispose()Audio、Video释放已解除绑定的 Output;返回错误码,成功后不能继续使用该实例

Output 状态

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

enum TiCloudStorageVideoOutputState {
  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<TiCloudStorageSnapshotFile>> takeSnapshot();

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

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

七、TiCloudStorageRecordingTask

dart
final class TiCloudStorageRecordingTask {
  TiCloudStorageRecordingTask._();

  Future<Resp<TiCloudStorageRecordingFile>> 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。页面显示录制秒数时,由应用从开始动作自行计时;最终准确时长以 TiCloudStorageRecordingFile.duration 为准。

八、TiCloudStorageExportTask

dart
final class TiCloudStorageExportTask {
  TiCloudStorageExportTask._();

  double get progress;

  Future<Resp<TiCloudStorageRecordingFile>> get result;

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

TiCloudStorageExportTask 只能由设备实例的 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<TiCloudStorageRecordingFile>> get result;

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

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

stop

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

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

九、生命周期与回调

并发规则

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

回调与关闭

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

十、错误码

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

错误码Flutter 常量常见含义建议处理
0kTiCloudStorageErrorOk操作成功继续后续流程
6000kTiCloudStorageErrorInvalidArgument参数缺失、取值越界或时间范围无效修正参数后重试
6001kTiCloudStorageErrorNotInitialized尚未初始化 TiCloudStorage,或已经关闭先调用 TiCloudStorage.init
6014kTiCloudStorageErrorTokenExpiredTi 云存 Token 已过期为同一设备更新 Token 后重新发起请求
6022kTiCloudStorageErrorAlreadyInitialized已使用另一组配置初始化保持初始化参数一致,或在全部资源释放后重新初始化
6024kTiCloudStorageErrorPermissionDeniedToken 无效、无权访问目标录像,或应用尚未取得系统相册写入权限检查对应授权;云端访问失败时重新取得 Token
6026kTiCloudStorageErrorInUse对象仍在播放、录制或执行任务,当前操作不能执行先结束活动操作,再重试
6027kTiCloudStorageErrorNotStarted尚未开始播放或录制先启动对应操作
6029kTiCloudStorageErrorNotBound对象尚未绑定 Replay完成 attach 后重试
6030kTiCloudStorageErrorNotConfigured尚未选择媒体通道或完成必要配置补充通道或配置后重试
6043kTiCloudStorageErrorResourceExhausted当前设备没有足够资源创建或继续任务结束其他任务并释放资源后重试
6046kTiCloudStorageErrorFileWriteFailed无法创建或写入输出文件检查目录权限和磁盘空间
6113kTiCloudStorageErrorUnsupportedFormat录像的音视频格式不受支持提示当前录像无法播放或导出
6114kTiCloudStorageErrorIoFailed底层 I/O 或运行时边界失败保留错误码并检查运行环境
6115kTiCloudStorageErrorCancelledList 请求被取消结束对应等待
6117kTiCloudStorageErrorRangeTooLargeList 查询跨度超过 10 天或合并结果超过 10000 项缩短查询范围后重试
6118kTiCloudStorageErrorNoFrameVideo Output 尚无可用于截图的视频帧等待出画后重试
6119kTiCloudStorageErrorNoRecordableMedia所选范围或通道没有可写入的媒体重新选择录像范围或通道
6120kTiCloudStorageErrorRecordingOverrun本地写入持续跟不上回放数据,保存任务已经终止释放设备资源、检查存储性能后重新保存
6122kTiCloudStorageErrorRecordingUnreadable录像文件缺失、损坏或无法读取提示当前录像不可用,不要持续重试同一请求
6123kTiCloudStorageErrorUnavailable网络、Ti 云存服务或录像数据读取暂时不可用稍后重试
6124kTiCloudStorageErrorStopped活动 ExportTask 已被主动停止结束导出进度,不使用输出文件

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

Ti 云存开发文档