Skip to content

Android API 说明 ​

Android 通过 TiRTC SDK 查询、回放、截图和导出云录像。

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

包名:

kotlin
import android.content.Context
import android.net.Uri
import android.view.ViewGroup
import com.tange.ai.tirtc.*

同步命令返回 Int 错误码,TiStoreErrorCode.OK 表示当前调用成功。Kotlin 的一次性异步操作使用 suspend 返回具体 Result;Java 使用对应 callback。需要业务分支处理的错误通过 TiStoreErrorCode 提供公开常量;完整含义和处理建议见错误码。TiStore.errorToString(code) 只用于展示或诊断。

基础类型 ​

kotlin
data class TiStoreRecordingRange(
    val startTimeMs: Long,
    val endTimeMs: Long,
)

data class TiStoreRecordingDay(
    val date: String,
    val hasRecording: Boolean,
)

enum class TiStoreReplaySpeed { X1, X2, X4, X8 }

data class TiStoreRecordingRangesResult(
    val code: Int,
    val recordings: List<TiStoreRecordingRange>,
)

fun interface TiStoreRecordingsCallback {
    fun onResult(result: TiStoreRecordingRangesResult)
}

data class TiStoreRecordingDaysResult(
    val code: Int,
    val days: List<TiStoreRecordingDay>,
)

fun interface TiStoreRecordingDaysCallback {
    fun onResult(result: TiStoreRecordingDaysResult)
}

fun interface TiStoreTimeChangedListener {
    fun onTimeChanged(timeMs: Long)
}

fun interface TiStoreReplayErrorListener {
    fun onError(code: Int)
}

fun interface TiStoreReplayCompletedListener {
    fun onCompleted()
}

fun interface TiStoreTokenExpiredListener {
    fun onTokenExpired()
}

TiStoreRecordingRange ​

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

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

TiStoreRecordingDay ​

字段说明
date严格的 YYYY-MM-DD 日期
hasRecording该自然日至少有一个当前可见的录像时点时为 true

TiStoreReplaySpeed ​

枚举值说明
X11 倍速,播放音频和视频
X22 倍速,视频播放,音频静音
X44 倍速,视频播放,音频静音
X88 倍速,视频播放,音频静音

Result、Callback 与 Listener ​

类型参数说明
TiStoreRecordingRangesResultcode 与 recordingslistRecordings 的一次性结果;成功但没有录像时列表为空
TiStoreRecordingsCallbackresultJava 调用 listRecordings 时接收同一个一次性结果
TiStoreRecordingDaysResultcode 与 dayslistRecordingDays 的一次性逐日结果
TiStoreRecordingDaysCallbackresultJava 调用 listRecordingDays 时接收同一个一次性结果
TiStoreTimeChangedListenertimeMs:当前播放位置,UTC Unix 毫秒回放位置变化通知
TiStoreReplayErrorListenercode:回放错误码Token、权限、网络或录像读取错误通知
TiStoreReplayCompletedListener无当前 play 的时间范围自然耗尽通知

Result 只返回一次方法调用的最终结果。Callback 是 Java 接收同一结果的入口;Listener 绑定对象,在对象生命周期内可以收到多次状态或事件通知。

最小回放示例 ​

selectedRecording 是从 listRecordings 返回结果中选择的一段录像。回放期间保持 replay、videoOutput 和 audioOutput 有效。

kotlin
val initCode = TiStore.init(applicationContext, appId)
if (initCode != TiStoreErrorCode.OK) return

val store = TiStore(token)
val replay = store.createReplay()
val videoOutput = TiStoreVideoOutput()
val audioOutput = TiStoreAudioOutput()
replay.onError = TiStoreReplayErrorListener { code ->
    showReplayError(code)
}
replay.onCompleted = TiStoreReplayCompletedListener {
    showReplayCompleted()
}

videoOutput.attachView(videoContainer)
videoOutput.attach(replay, videoChannelId)
audioOutput.attach(replay, audioChannelId)

replay.play(
    selectedRecording.startTimeMs,
    selectedRecording.endTimeMs,
)

同步方法返回非 TiStoreErrorCode.OK 时,当前操作没有生效。通过两个 Output 的状态更新播放界面;Token、网络或录像读取失败时,在 Replay 的错误 listener 中处理。

页面退出时,先停止 Replay、detach 两个 Output,并调用 videoOutput.detachView()。再依次 dispose Output 和 Replay。不再访问这台设备时,dispose store。

TiStore ​

kotlin
class TiStore(token: String) {
    companion object {
        @JvmStatic
        @JvmOverloads
        fun init(
            context: Context,
            appId: String,
            endpoint: String = "",
            consoleLogEnabled: Boolean = false,
        ): Int

        @JvmStatic
        fun errorToString(code: Int): String
    }

    var onTokenExpired: TiStoreTokenExpiredListener?

    fun updateToken(token: String): Int

    @JvmSynthetic
    suspend fun listRecordingDays(
        startDate: String,
        endDate: String,
        timeZoneId: String = "Asia/Shanghai",
    ): TiStoreRecordingDaysResult

    fun listRecordingDays(
        startDate: String,
        endDate: String,
        callback: TiStoreRecordingDaysCallback,
    )

    fun listRecordingDays(
        startDate: String,
        endDate: String,
        timeZoneId: String,
        callback: TiStoreRecordingDaysCallback,
    )

    @JvmSynthetic
    suspend fun listRecordings(
        startTimeMs: Long,
        endTimeMs: Long,
    ): TiStoreRecordingRangesResult

    fun listRecordings(
        startTimeMs: Long,
        endTimeMs: Long,
        callback: TiStoreRecordingsCallback,
    )

    fun createReplay(): TiStoreReplay

    fun exportRecording(
        request: TiStoreExportRequest,
        progressListener: TiStoreExportProgressListener?,
        callback: TiStoreRecordingCallback,
    ): TiStoreExportTaskStartResult

    fun dispose(): Int
}

使用任何云存储 API 前先调用 TiStore.init。初始化后用一台设备的非空 Token 创建 TiStore 实例;构造不发起网络请求。同时访问多台设备时创建多个实例。

API说明
init(context, appId, endpoint, consoleLogEnabled)初始化。context 传 applicationContext;appId 来自申请开通;endpoint 留空时使用默认服务地址;consoleLogEnabled 默认为 false。返回本次初始化错误码
errorToString(code)返回错误码对应的稳定名称,用于日志和诊断
updateToken(token)为同一设备更新 Token,只影响之后启动的 List、Play 和 Export
listRecordingDays(startDate, endDate, timeZoneId)Kotlin 查询包含式日期范围,单次最多 31 天。时区使用 IANA ID,默认 Asia/Shanghai;结果按日期升序返回范围内每一天
listRecordingDays(startDate, endDate, callback)Java 使用默认时区查询,结果在主线程返回一次
listRecordingDays(startDate, endDate, timeZoneId, callback)Java 使用显式 IANA 时区查询,结果在主线程返回一次
listRecordings(startTimeMs, endTimeMs)Kotlin 查询当前设备在 [startTimeMs, endTimeMs) 内的录像可用时间段,挂起并返回唯一的 TiStoreRecordingRangesResult。时间使用 UTC Unix 毫秒,单次跨度最长 10 天
listRecordings(startTimeMs, endTimeMs, callback)Java 互操作入口。包括参数、Token、对象状态和运行期错误在内,均由 callback 在主线程恰好返回一次,不同步返回另一份错误码
createReplay()创建属于当前设备的 Replay
exportRecording(request, progressListener, callback)开始一次录像导出。进度监听器可空;同步结果包含错误码和可空 Task,接受成功后 callback 返回唯一的最终结果
dispose()释放当前设备实例;仍有查询、Replay、ExportTask 或回调正在执行时返回 in-use

onTokenExpired 只在实例当前 Token 被云端明确判定为过期时通知,同一个 Token 至多一次。触发过期的 List、Play 或 Export 先通过自身结果或 listener 报告错误;Token 无效或权限不足不触发该 listener。当前 Token 已经确定过期时,新的 List 通过唯一 Result 返回 TOKEN_EXPIRED,新的 Play 或 Export 在启动调用中同步拒绝。updateToken 保存不同的非空 Token,之后启动的操作使用新 Token;相同 Token 不清除过期状态,已经开始的操作也不自动换证或重试。它只用于同一设备续签,访问另一台设备时创建新的 TiStore。

listRecordingDays 的起止日期都使用严格的 YYYY-MM-DD 格式并包含在结果中。成功结果包含范围内每一天,包括 hasRecording == false 的日期。时区必须是有效的 IANA ID;范围超过 31 天时返回 range-too-large。

listRecordings 结果按 startTimeMs 升序排列,并裁剪在请求的 [startTimeMs, endTimeMs) 内。重叠或首尾相接的时间段会合并;任何大于 0 的空洞都会保留为不同项。结果是设备级完整快照,不保证每个 Channel 都有媒体,也不分页。合并后最多返回 10000 项。查询跨度或结果数量超限时,整体返回 range-too-large,不返回部分结果;成功但没有录像时返回空列表。

TiStoreReplay ​

kotlin
class TiStoreReplay private constructor() {
    val speed: TiStoreReplaySpeed
    val currentTimeMs: Long?

    var onTimeChanged: TiStoreTimeChangedListener?
    var onError: TiStoreReplayErrorListener?
    var onCompleted: TiStoreReplayCompletedListener?

    fun play(
        startTimeMs: Long,
        endTimeMs: Long,
        initialTimeMs: Long? = null,
    ): Int

    fun pause(): Int
    fun resume(): Int
    fun seek(timeMs: Long): Int
    fun setSpeed(speed: TiStoreReplaySpeed): Int
    fun startRecording(
        videoChannelId: Int,
        audioChannelId: Int? = null,
    ): TiStoreRecordingTaskStartResult
    fun stop(): Int
    fun dispose(): Int
}

属性与 Listener ​

成员说明
speed当前回放倍速,初始值为 X1
currentTimeMs当前回放位置,UTC Unix 毫秒;首次取得位置前和 stop 后为 null
onTimeChanged回放位置变化通知;晚设置时不会补发之前的事件
onError当前 play 的 Token、权限、网络或录像读取错误;一次回放最多通知一次
onCompleted当前 play 的时间范围自然耗尽通知;一次回放最多通知一次

方法 ​

API说明
play(startTimeMs, endTimeMs, initialTimeMs)播放所属设备在 [startTimeMs, endTimeMs) 内的录像。initialTimeMs 可空,缺省时从 startTimeMs 开始;传入时从指定位置开始。返回值表示请求是否被接受
pause()暂停当前回放;重复调用成功
resume()继续当前回放;重复调用成功
seek(timeMs)定位到当前回放范围内的 UTC 时间;同步返回操作是否被接受
setSpeed(speed)设置 X1、X2、X4 或 X8 倍速;非 X1 时 Audio Output 静音
startRecording(videoChannelId, audioChannelId)保存当前回放的一段内容。同步结果包含错误码和可空 RecordingTask
stop()停止当前回放并清除 currentTimeMs;保留 Output 绑定和倍速
dispose()释放 Replay;调用后不能继续使用该实例

Replay 只能由 store.createReplay() 创建,并固定属于该设备实例。Replay 没有公开 state。onTimeChanged 返回当前播放时间;onError 返回当前 play 过程中发生的 Token、权限、网络或录像读取错误。错误发生时恰好调用一次;同步拒绝、主动 stop、被新 play 替换或自然完成不会触发。每个被接受的 play 仅在其时间范围自然耗尽时调用一次 onCompleted;同步拒绝、主动 stop、被新 play 替换或错误终止均不触发。开始 play 前设置 listener,晚设置不会重放此前错误或完成事件。接受 Play 时固定使用 Store 实例当前的 Token,之后更新 Token 不改变本次回放。

录像文件已被删除、缺失、损坏或无法读取时,onError 返回 TiStoreErrorCode.RECORDING_UNREADABLE。网络或服务暂时不可用时,返回 TiStoreErrorCode.STORE_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 保留速度和暂停意图,连续调用采用 latest-wins。定位生效后,currentTimeMs 和 onTimeChanged 更新为第一个不早于目标的可播放位置。目标合法但其后没有录像时,位置推进到 endTimeMs 并按自然结束触发 onCompleted。
  • stop 幂等,不触发 onCompleted,也不把 Output 标记为 COMPLETED;它会清除 currentTimeMs,并保留 Output 绑定和速度。

Audio/Video Output ​

kotlin
enum class TiStoreAudioOutputState {
    IDLE,
    BUFFERING,
    PLAYING,
    FAILED,
    PAUSED,
    COMPLETED,
}

enum class TiStoreVideoOutputState {
    IDLE,
    BUFFERING,
    RENDERING,
    FAILED,
    PAUSED,
    COMPLETED,
}

fun interface TiStoreAudioOutputStateListener {
    fun onStateChanged(state: TiStoreAudioOutputState)
}

fun interface TiStoreVideoOutputStateListener {
    fun onStateChanged(state: TiStoreVideoOutputState)
}

fun interface TiStoreOutputErrorListener {
    fun onError(code: Int)
}

class TiStoreAudioOutput {
    val state: TiStoreAudioOutputState
    var onStateChanged: TiStoreAudioOutputStateListener?
    var onError: TiStoreOutputErrorListener?

    fun attach(replay: TiStoreReplay, channelId: Int): Int
    fun setVolume(volumePercent: Int): Int
    fun detach(): Int
    fun dispose(): Int
}

class TiStoreVideoOutput {
    val state: TiStoreVideoOutputState
    var onStateChanged: TiStoreVideoOutputStateListener?
    var onError: TiStoreOutputErrorListener?

    fun attachView(container: ViewGroup): Int
    fun detachView(): Int
    fun attach(replay: TiStoreReplay, channelId: Int): Int

    @JvmSynthetic
    suspend fun takeSnapshot(): TiStoreSnapshotResult
    fun takeSnapshot(callback: TiStoreSnapshotCallback)

    fun detach(): Int
    fun dispose(): Int
}

setVolume 接收 0..100;0 表示静音。它只调整当前 Audio Output,不修改系统全局音量。新一次 play 会保留该值。

Output 状态 ​

Audio 状态Video 状态说明
IDLEIDLE尚未输出媒体,或已经 detach
BUFFERINGBUFFERING正在等待足够的媒体数据
PLAYINGRENDERING正在播放音频或渲染视频
PAUSEDPAUSEDReplay 已暂停
COMPLETEDCOMPLETEDReplay 来源已经自然耗尽,并且该 Output 已排空
FAILEDFAILED当前 Output 无法继续输出媒体

属性与 Listener ​

成员说明
state当前 Output 状态
onStateChangedOutput 状态变化通知;参数 state 为当前 Output 状态
onError当前 Output 的解码、播放或渲染错误;参数 code 为错误码,不会报告 Token、网络或录像读取错误

方法 ​

API适用对象说明
attachView(container)Video把 Video Output 绑定到宿主提供的 ViewGroup;重复绑定同一容器成功,换绑失败时保留原容器
detachView()Video解除宿主 View 绑定;对象和 Replay 绑定保持不变
attach(replay, channelId)Audio、Video绑定 Replay 中指定 channel_id 的音频或视频;channelId 取值为 0..255
takeSnapshot() / takeSnapshot(callback)Video把当前视频画面保存为唯一的临时 JPEG;成功结果包含 TiStoreSnapshotFile
detach()Audio、Video解除当前 Replay 绑定;对象仍可再次 attach
dispose()Audio、Video释放 Output;调用后不能继续使用该实例

attachView 与 Replay 的 attach 相互独立;SDK 借用 ViewGroup 到 detachView 成功为止,应用必须在销毁或复用容器前先解除绑定。Kotlin takeSnapshot() 挂起到最终结果返回,Java callback 在主线程调用一次。成功时 file.path 是 SDK 私有 cache 中的临时 JPEG。

当前绑定还没有画面时,callback 返回 no-frame。暂停或自然播放完成后,只要最后画面仍然保留,就可以继续截图;attach 新来源、新的 play 生效、detachView 或 detach 后,在新画面到达前返回 no-frame。同一个 Video Output 同时只接受一个 Snapshot;已有请求未完成时,新的 callback 返回 in-use。不同 Output 仍可能因全局资源上限返回 resource-exhausted。

Snapshot callback 返回前,dispose() 返回 in-use,Output、View 与 Replay 关系保持不变。先等待 callback,再按正常顺序解除 View 和 Replay 绑定并释放 Output。

Video Output 和 Audio Output 分别使用设备配置提供的 channelId,取值范围是 0..255;两者可以相同,也可以不同。使用 exhaustive when 时,需要处理 PAUSED 和 COMPLETED。

重复绑定同一个 Replay 的同一个 channel_id 会成功;已经绑定时传入不同 Replay 或 channel_id 返回 in-use 并保留原关系,必须先显式 detach。Replay 正在运行时新增或 detach 后重新绑定 Output,从绑定生效后的下一可独立解码位置开始,不补发此前媒体。同一个 Replay 的同一个 channel_id 只能绑定一个同类播放 Output。stop、自然完成和新 play 都保留绑定;dispose 不隐式解绑,关系仍存在时返回 in-use。某个 Output 解码、播放或渲染失败时,它的错误 listener 返回错误,并且只终止该 Output。Token、网络、所选范围过大或录像读取失败时,Replay 的错误 listener 返回一次错误,并使本次回放的全部 Output 进入 FAILED。

本地媒体文件 ​

kotlin
fun interface TiStoreDeleteCallback {
    fun onResult(code: Int)
}

class TiStoreRecordingFile internal constructor(
    val path: String,
    val durationMs: Long,
) {
    @JvmSynthetic suspend fun delete(): Int
    fun delete(callback: TiStoreDeleteCallback)
}

class TiStoreSnapshotFile internal constructor(val path: String) {
    @JvmSynthetic suspend fun delete(): Int
    fun delete(callback: TiStoreDeleteCallback)
}

data class TiStoreRecordingResult(
    val code: Int,
    val file: TiStoreRecordingFile?,
)

data class TiStoreSnapshotResult(
    val code: Int,
    val file: TiStoreSnapshotFile?,
)

fun interface TiStoreRecordingCallback {
    fun onResult(result: TiStoreRecordingResult)
}

fun interface TiStoreSnapshotCallback {
    fun onResult(result: TiStoreSnapshotResult)
}

文件路径位于 SDK 私有 cache。Android SDK 只提供受限、幂等的 delete();需要长期保存时由应用自行复制或移动文件。系统相册便利能力只由 Flutter 与 React Native SDK 提供。

TiStoreRecordingTask ​

kotlin
data class TiStoreRecordingTaskStartResult(
    val code: Int,
    val task: TiStoreRecordingTask?,
)

class TiStoreRecordingTask {
    @JvmSynthetic
    suspend fun stop(): TiStoreRecordingResult
    fun stop(callback: TiStoreRecordingCallback)
}

TiStoreReplay.startRecording(videoChannelId, audioChannelId) 同步返回 TiStoreRecordingTaskStartResult。Replay 必须正在回放;视频 Channel 必填,音频 Channel 可空。成功时 code == OK 且 task 非空;同步失败时不创建 Task。

Kotlin stop() 与 Java stop(callback) 都停止接收新媒体,排空已经接收的数据并完成 MP4。重复或并发调用取得同一个最终结果。Replay 自然结束时 Task 自动完成,之后调用 stop 返回缓存结果。

Task 不公开 state、实时 duration、cancel 或 dispose。停止一个 Task 不影响 Replay 或其他 Task。相同 Channel 可以创建多个 Task,SDK 为每个 Task 生成不同文件;两个视频 Channel 使用两个 Task。

TiStoreExportTask ​

kotlin
data class TiStoreExportRequest(
    val startTimeMs: Long,
    val endTimeMs: Long,
    val videoChannelId: Int,
    val audioChannelId: Int? = null,
)

data class TiStoreExportTaskStartResult(
    val code: Int,
    val task: TiStoreExportTask?,
)

fun interface TiStoreExportProgressListener {
    fun onProgress(progress: Double)
}

class TiStoreExportTask {
    val progress: Double
    fun stop(): Int
}

store.exportRecording(request, progressListener, callback) 在启动 Task 前固定实例当前的 Token,并保存两个回调。随后同步返回 TiStoreExportTaskStartResult。成功时 Task 已经开始,进度与最终结果在主线程返回。

同步失败时 task == null,且不调用回调。SDK 也不会在 exportRecording 返回前调用回调。

progress 位于 0.0..1.0 且单调不回退。只有 MP4 完整生成后才到达 1.0。中间通知可以合并,progress 始终返回当前快照。

活动 Task 调用 stop() 会停止导出、清理临时文件,并使同一个最终结果 callback 返回 STOPPED 和 file == null;它不生成部分 MP4。已经自然完成或失败时,stop() 返回 OK 且不改变缓存结果。重复调用幂等。

Task 在最终结果 callback 返回后自动释放底层资源,不提供 dispose。不同 Task 相互独立并生成不同 cache 文件;资源达到上限返回 RESOURCE_EXHAUSTED。

错误码 ​

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

常量列为“—”的错误通常表示调用顺序或资源状态问题,记录数值用于定位即可,不需要在业务流程中逐项判断。不要根据 errorToString 返回值或日志文本判断错误类型。

线程、生命周期与 Java 互操作 ​

  • 所有 callback/listener 都进入 Android 主线程。
  • SDK 不在发起 API 调用的栈内同步调用 callback。
  • Kotlin listRecordings 返回到调用方 coroutine;@JvmSynthetic 入口不向 Java 暴露。
  • callback 参数是不可变的 Kotlin/Java 值对象。
  • dispose 会检查对象是否仍在使用,并允许在清理后重试。Replay 仍在播放、仍有 Output 绑定或活动 RecordingTask 时返回 in-use。Output 仍绑定 Replay、宿主 View 或活动 Snapshot 时也返回 in-use。失败时对象及关系保持不变。
  • dispose 只有在 native destroy 真正成功后才返回 OK;重复调用幂等返回 OK。成功后普通方法返回 not-initialized,getter 保留最后提交值,SDK 不再投递该对象的普通 listener。
  • MP4 Task 在最终结果 callback 返回后自动释放底层资源;Java/Kotlin 不需要额外 dispose。
  • TiStore 和 Output 的构造器只创建对应对象,不抛 SDK 运行错误;createReplay 同样只创建 Replay。第一次需要底层资源的方法返回创建错误,对象仍可在修正问题后重试。

Java 可以直接使用一次性 callback;不关心导出进度时无需实现额外接口:

java
TiStore store = new TiStore(token);
store.listRecordings(startTimeMs, endTimeMs, result -> {
    if (result.getCode() == TiStoreErrorCode.OK) {
        showRecordings(result.getRecordings());
    }
});

TiStoreExportTaskStartResult exportStart = store.exportRecording(
    request,
    null,
    result -> showExportResult(result)
);

TiStore 开发文档