HappyOyster Android SDK 入口為單例物件 HappyOyster,除 initialize、updateToken、attachVideo、sendCommand 外,業務方法均為 suspend,失敗時擲回 SDKError。
當呼叫端 coroutine 被取消時,SDK 會取消該呼叫仍在進行的 HTTP 請求(如有),並原樣傳播 CancellationException,不會轉換為 SDKError。取消並不代表伺服器已經受理的請求會被回滾。
整合流程、安裝、最佳實務請參閱 HappyOyster Android SDK 整合指南。
核心名詞概念
名詞 | 含義 |
|---|---|
token | 百煉閘道 API Key:由您的 App 透過 |
ticket | 一次性體驗憑證:由您的伺服器呼叫開放平台換取並下發給用戶端,僅用於一次 |
環境需求
項目 | 要求 |
|---|---|
minSdk | 24(Android 7.0)及以上 |
compileSdk | 36 |
語言 | Kotlin(協程 |
ABI |
|
網路 | 需要可存取公網 |
核心概念
概念 | 說明 |
|---|---|
World | AI 世界,包含角色與場景。由您的伺服器建立與管理。 |
Travel | 一次即時體驗。基本生命週期: |
模式 |
|
即時視訊 |
|
概覽
HappyOyster 方法
方法 | 說明 |
|---|---|
| 初始化 SDK;允許在 idle 時重新初始化,若 Travel 正在 starting、active 或 ending 時重新初始化,將以 |
| 注入/更新百煉閘道 API Key(Bearer token)。 |
| 使用一次性憑證開始體驗,並自動建立即時視訊連線。 |
| 非同步暫停體驗(即時導演與角色演繹,且體驗支援暫停);受理後真正暫停以 |
| 恢復已暫停的體驗(即時導演與角色演繹);內部包含 3× 重試退避機制。 |
| 回溯到指定秒數(僅即時導演、狀態為 paused)。秒數須為 4 的整數倍。角色演繹 不支援回溯。 |
| 傳送文字指令以驅動劇情(即時導演與角色演繹,running 或 paused 狀態)。 |
| 傳送方向/視角/動作控制指令(世界探索模式,僅限 running 狀態;需於主執行緒呼叫)。角色演繹模式不可用。 |
| 傳回用於播放的 |
| 結束體驗,自動中斷即時連線並釋放所有工作階段資源。 |
| SDK 版本字串(SemVer),編譯期常數。 |
事件
事件 | 說明 |
|---|---|
| 體驗狀態變更(包含即時視訊生命週期),值請參閱 |
| 內部自動流程發生錯誤時回呼;致命錯誤會同時終止本次體驗。 |
關鍵資料類型
類型 | 說明 |
|---|---|
| SDK 初始化設定( |
| 體驗狀態: |
| 體驗模式: |
| 世界建立模型: |
|
|
|
|
| SDK 錯誤,包含 |
HappyOyster.initialize
初始化 SDK,應在呼叫其他 API 前完成(建議在 Application.onCreate)。config 為必填參數,須攜帶您帳號的百煉 apiHost,並透過 model 指定拆分後的模型名稱(happyoyster-1.0-directing / happyoyster-1.0-acting / happyoyster-1.0-adventure,與所用 Open API 入口一致)。SDK 按以下規則拼接請求位址:
https://{apiHost}/api/v2/apps/{model}/openapi/v1/{endpoint}
model 沒有預設值:建構 SDKConfig 時遺漏該參數會編譯失敗;傳入空白值則 initialize 同步擲回 SDKError(100002),且不會建立或替換 runtime,也不會發起網路請求。SDK 只持有 application context,不持有 Activity。僅當上一個 runtime 為 idle 時允許重新初始化;切換模型時須在 idle 狀態使用新 model 重新初始化。若 Travel 正在 starting、active 或 ending,須先等待 endTravel() 完成。
說明
地區合規提示:- 為支援適用法律法規及資料合規要求的落實,若您的目標使用者包含美國使用者,您必須在 SDK 初始化時為面向該等使用者的服務設定美國地區 API Host。
- 開發者應確保設定正確,並依法承擔因未依上述要求設定所產生的相應責任。
簽章
fun initialize(context: Context, config: SDKConfig)
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
context | Context | 是 | 建議傳入 |
config | SDKConfig | 是 | SDK 設定,必須包含必填且無預設值的 |
傳回值
無回傳值。
錯誤
code | 說明 |
|---|---|
|
|
| Travel 正在 starting、active 或 ending,拒絕重新初始化。請等待 |
HappyOyster.updateToken
注入/更新百煉閘道 API Key,執行緒安全;在 initialize 後可隨時呼叫。SDK 會將最新 token 用於 startTravel 及後續體驗控制請求。ticket 是一次性體驗憑證,不應與 Bearer token 混用。
簽章
fun updateToken(token: String)
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
token | String | 是 | 百煉閘道 API Key,作為 Bearer token 使用。 |
傳回值
無回傳值。
錯誤
code | 說明 |
|---|---|
| SDK 尚未初始化。 |
HappyOyster.startTravel
使用一次性 ticket 開始一次體驗。成功後 SDK 自動建立即時視訊連線並開始內部狀態輪詢,狀態透過 onStatusChanged 透出。
ticket為一次性憑證,呼叫後即視為已消耗。- 同一 App 同一時刻僅允許一個併發 Travel;已有體驗進行中時再次呼叫會擲回
SDKError(103004),SDKError.raw中僅包含當前活躍 ticket 的去識別化摘要供診斷,不包含完整 ticket。 - 可選擇傳入
maxExperienceTimeSec限制本次體驗最大時長(僅世界探索 / adventure 模式生效),詳見參數。
StartTravelData.creationModel(CreationModelValue,預設 CreationModelValue.Simple):指示該世界的建立模型,即劇本內容的管理方式。simple(預設)為普通 instruct 驅動的世界,世界探索類世界也會標準化為此值。scriptlist 為結構化 ScriptList 世界——劇本內容不透過本 SDK 存取。在 ScriptList 模式(creationModel == CreationModelValue.ScriptList)下呼叫 sendInstruct 會被拒絕,並擲回 SDKError(103002)。該欄位預設值為 simple。
StartTravelData.aspectRatio(String?):伺服器為本次工作階段分配的播放畫幅,格式如 width:height。僅角色演繹(acting)非空——"9:16"(直式,伺服器建立時的預設值)或 "16:9"(橫式);世界探索與即時導演模式、以及伺服器未下發時為 null。SDK 原樣保留未知取值(不收斂為 null),宿主須將不認識的值等同 null 處理並回退到自己的預設方向;SDK 自身不使用該欄位。
該值隨 startTravel() 的返回值交付:請在 startTravel() 返回後、呼叫 attachVideo() 把返回的檢視掛進版面配置之前據此確定播放容器方向——SDK 只有在宿主掛載檢視後才開始綁定渲染遠端串流,所以此刻定方向仍趕在首幀渲染之前(但不保證發生在 SDK 加入即時房間之前)。遠端檢視以裁剪填充(clip-to-fill)方式綁定,容器方向與該值不一致會裁掉畫面而不是留黑邊。
簽章
suspend fun startTravel(ticket: String): StartTravelData
suspend fun startTravel(ticket: String, maxExperienceTimeSec: Int?): StartTravelData
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
ticket | String | 是 | 一次性體驗憑證,由您的伺服器端下發。 |
maxExperienceTimeSec | Int? | 否 | 本次體驗的最大時長(秒),到期後世界端自動結束工作階段。僅世界探索(adventure)模式生效,即時導演與角色演繹忽略此值。合法取值由伺服器設定,當前為 |
傳回值
傳回 StartTravelData,包含體驗中繼資料(mode、version、creationModel、aspectRatio 等)。請先檢視傳回的中繼資料再決定如何互動(如 travel.mode、travel.creationModel、travel.version),角色演繹還需按 travel.aspectRatio 設定播放容器方向。
錯誤
code | 說明 |
|---|---|
|
|
|
|
| 輸入參數無效(如 |
| 世界狀態非就緒 |
| 資源分配/服務內部失敗 |
| 已有 Travel 正在 starting、active 或 ending,或併發呼叫 |
HappyOyster.pauseTravel / HappyOyster.resumeTravel
暫停 / 恢復體驗。SDK 同時僅管理一個 Travel,encryptedTravelId 由 SDK 內部自動取得,呼叫端無需傳入。
前置條件:
pauseTravel:僅即時導演(directing)或角色演繹(acting)、且狀態為running時可呼叫。resumeTravel:僅即時導演(directing)或角色演繹(acting)、狀態為paused時可呼叫。
並非所有體驗都支援暫停 / 恢復:體驗須報告本模式要求的版本識別碼(StartTravelData.version,即時導演為 storyV2、角色演繹為 actingV2;比較時忽略首尾空白與大小寫),否則 pauseTravel 與 resumeTravel 都會返回 103002。角色演繹支援暫停 / 恢復,但 不支援 rewindTravel。
暫停是非同步的(重要):pauseTravel 是一個「重」操作——呼叫成功(方法返回)只代表暫停已受理,此刻體驗尚未真正暫停。只有當 onStatusChanged 回呼報告 paused 時,才算真正暫停。因此請以該回呼驅動宿主狀態機和呼叫門控:在「呼叫 pauseTravel」到「收到 paused 回呼」之間可將本機狀態標記為 pausing;收到 paused 後才允許發起 resumeTravel 或 rewindTravel。不要把 pauseTravel 的返回當作已暫停。
即時連線的處理:真正暫停後 SDK 會斷開即時連線;恢復時 SDK 會使用同一次 startTravel 時下發的憑證自動重新加入並恢復畫面,無需宿主干預。
暫停拆房有序屏障:收到 Paused 後,伺服器即時房间的拆除仍可能短暫延遲。SDK 會從暫停確認時刻起建立 3 s settle 窗口;窗口內呼叫 resumeTravel 或 rewindTravel 時,suspend 呼叫先非阻塞等待剩餘時間,再傳送會重新開房的 API。若收到 Paused 後已自然等待滿 3 s,則不增加延遲。這樣可避免遲到的 pause teardown 關閉剛重開的房間並觸發 105001。
resume API 重試:resumeTravel 內部會在失敗時最多重試 3 次(退避 1 s / 2 s / 3 s),以應對暫停後服務短暫不可用的情況;只有 3 次全部失敗才會向上擲回錯誤。
說明建議宿主在 resumeTravel 返回成功後 3 s 內暫緩再次發起 pauseTravel,避免過於頻繁的切換。此為宿主端呼叫冷卻建議,SDK 本身不強制。詳見 HappyOyster Android SDK 整合指南。
簽章
suspend fun pauseTravel(): TravelStateData
suspend fun resumeTravel(): TravelStateData
參數
無參數。
傳回值
傳回 TravelStateData(包含 encryptedTravelId、status)。
錯誤
code | 說明 |
|---|---|
| 無活躍體驗 |
| 狀態不允許,或該體驗不支援暫停/恢復 |
| 在世界探索模式下呼叫,僅即時導演與角色演繹支援暫停/恢復 |
HappyOyster.rewindTravel
回溯至指定秒數。encryptedTravelId 由 SDK 內部自動取得。
前置條件:僅即時導演(directing)模式、且狀態為 paused 時可呼叫(不允許在 running 狀態下回溯);並非所有即時導演體驗都支援回溯,不支援時返回 103002。回溯成功後體驗會自動恢復,SDK 會自動重連 RTC,無需宿主干預。
角色演繹完全沒有回溯能力:在角色演繹下呼叫會被 SDK 在本機以 103003 拒絕,不發出任何請求。宿主在角色演繹下應隱藏回溯入口,而不僅是停用按鈕。
簽章
suspend fun rewindTravel(rewindToSec: Double): RewindTravelData
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
rewindToSec | Double | 是 | 回溯到的目標秒數,應為 4 的整數倍(如 4、8、12)。非 4 的整數倍將由伺服器向下取整至最近的較小 4 倍數(如傳 7 則取 4)。 |
傳回值
傳回 RewindTravelData(包含 encryptedTravelId、status、resumedAtSec)。其中 resumedAtSec 為伺服器實際回溯到的秒數(已按 4 的整數倍向下取整)。
錯誤
code | 說明 |
|---|---|
| 無活躍體驗 |
| 狀態不是 |
| 在世界探索或角色演繹模式下呼叫,僅即時導演模式支援回溯 |
HappyOyster.sendInstruct
傳送文字指令驅動劇情,僅在即時導演(directing)或角色演繹(acting)且體驗狀態為 running 或 paused 時有效。encryptedTravelId 由 SDK 內部自動取得。
paused 態行為:SDK 不會在 paused 時自動 resume,instruct 直接傳送。由宿主決定是否先呼叫 resumeTravel 再傳送 instruct。
簽章
suspend fun sendInstruct(content: String): SendInstructData
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
content | String | 是 | 要傳送的文字指令內容。 |
傳回值
傳回 SendInstructData(包含 encryptedTravelId、content、accepted)。
錯誤
code | 說明 |
|---|---|
| 無活躍體驗(未呼叫 |
| 目前模式非即時導演或角色演繹(在世界探索模式下呼叫) |
| 體驗狀態既不是 |
| 內容安全審核攔截 |
| Travel 不存在 |
HappyOyster.sendCommand
傳送方向/視角/動作控制指令。僅在世界探索模式且狀態為 running 時有效。
- 請在主執行緒呼叫;若離開主執行緒呼叫將同步拋出
IllegalStateException。 - 無活躍體驗回報
103001。 - 在非世界探索模式(即時導演/角色演繹)下呼叫會回報
103003。 - 狀態不允許回報
103002。 - 上行透過一路靜音音訊串流建立(SDK 不錄製、不上傳真實音訊);
RECORD_AUDIO並非 DataChannel 硬性前置條件,為相容各機型建議宣告並授予(見 HappyOyster Android SDK 整合指南 § 安裝 · 權限)。105004表示即時通道未就緒 / 傳送失敗(如 DataChannel 連線中斷或即時通道異常),並非由缺少權限直接觸發。
內建 42 ms 節流(24fps,latest-wins):sendCommand 內部對 DataChannel 寫入做了以 42 ms(約 24fps)為週期的節流。狀態驗證同步、立即執行並在無效呼叫時立即擲回;DataChannel 實際寫入是非同步節流的:若距上次傳送已過 42 ms 則立即發出(首次立即發),否則將命令更新為最新值(latest-wins)並在當前 42 ms 週期結束時延遲發出一次。同一週期內多次呼叫等同於一次、以最後一次為準。這意味著宿主可以按遊戲影格率高頻呼叫 sendCommand,SDK 會穩定合併為約每 42 ms 一次上鏈,無需宿主手動限速。
節流期間的傳送失敗:節流 flush 是非同步的,傳送失敗無法擲回給呼叫端——錯誤會透過 onError 回呼(非嚴重錯誤,105004)透出。Session 結束時 pending 的命令會被丟棄(不會在結束後傳送)。
簽章
fun sendCommand(command: AdventureCommand)
參數
欄位 | 類型 | 是否必填 | 描述 |
|---|---|---|---|
command | AdventureCommand | 是 | 包含 |
AdventureCommand 欄位與取值
translation — 移動
前/左/後/右/斜向/靜止。
值 | 方向 |
|---|---|
| 前 |
| 左 |
| 後 |
| 右 |
| 左前 |
| 右前 |
| 左後 |
| 右後 |
| 靜止 |
rotation — 視角
上/下/左/右/斜向/無。
值 | 方向 |
|---|---|
| 上 |
| 下 |
| 左 |
| 右 |
| 左上 |
| 右上 |
| 左下 |
| 右下 |
| 無 |
interaction — 互動
跳躍/攻擊/蹲下/衝刺/無。
值 | 動作 |
|---|---|
| 跳躍 |
| 攻擊 |
| 蹲下 |
| 衝刺 |
| 無 |
三欄位彼此獨立,各自對應一組互斥指令;斜向移動/視角使用單一組合值(如同時前+左傳送 W_A,而非同欄位併發 W 與 A)。每次呼叫傳入當前完整狀態即可。
最佳實務:單次動作 vs 持續動作
以 42 ms 週期作為心智模型,區分兩類用法:
單次動作(如點擊一下跳躍/攻擊、走一步):呼叫一次即可。SDK 會在最近的週期將其上鏈,動作隨即生效;無需持續傳送,也無需補發 None。
// 跳一下
HappyOyster.sendCommand(AdventureCommand("None", "None", "Jump"))
持續動作(如按住持續移動、持續轉視角):按住期間按影格持續呼叫,SDK 每約 42 ms 上鏈一次,持續輸出該指令,角色即持續動作;鬆手時明確傳送一次帶 None 的指令復位停止。SDK 不會自動代發 None——不持續呼叫就沒有後續上鏈、動作會停下,但不發 None 就不會主動復位。
// 按住:每帧持续调用(宿主自行驱动帧循环)
HappyOyster.sendCommand(AdventureCommand("W", "None", "None"))
// 松手:显式复位一次
HappyOyster.sendCommand(AdventureCommand("None", "None", "None"))
傳回值
無傳回值(同步)。狀態驗證同步立即執行;DataChannel 寫入採用非同步節流。
錯誤
code | 說明 |
|---|---|
| 無活躍體驗 |
| 狀態不允許 |
| 在非世界探索模式(即時導演/角色演繹)下呼叫 |
| 節流 flush 傳送失敗(非致命,非同步回呼) |
HappyOyster.attachVideo
傳回一個用於播放的 SurfaceView,由您加入版面配置;SDK 內部完成與遠端串流的渲染綁定。
- 請在主執行緒呼叫;若離開主執行緒呼叫將同步拋出
IllegalStateException。 - SDK 對傳回的 View 僅持有弱參考,體驗結束時釋放渲染綁定;您需自行將 View 從版面配置中移除。
- 角色演繹體驗請先按
StartTravelData.aspectRatio設定好播放容器方向,再將傳回的檢視掛入版面配置:渲染以裁剪填滿方式綁定,方向不符會裁掉畫面(見startTravel)。
簽章
fun attachVideo(): SurfaceView
參數
無參數。
傳回值
傳回 SurfaceView,加入版面配置後即可播放即時視訊。
錯誤
code | 說明 |
|---|---|
| SDK 未初始化 |
HappyOyster.endTravel
結束體驗。encryptedTravelId 由 SDK 內部自動取得。呼叫成功(或異常退出)後,SDK 自動斷開即時連線、停止內部輪詢、釋放全部工作階段資源,本次 ticket 同時失效。
簽章
suspend fun endTravel(): EndTravelData
參數
無參數。
傳回值
傳回 EndTravelData(包含 encryptedTravelId、status、endedAt、durationSec)。
錯誤
code | 說明 |
|---|---|
| SDK 未初始化 |
| 無活躍體驗 |
HappyOyster.VERSION
傳回 SDK 版本字串(SemVer),如 "x.y.z"。該值為編譯期常數,由 VERSION_NAME Gradle 屬性注入;無需先呼叫 initialize 即可安全讀取。
簽章
val VERSION: String
傳回值
SDK 版本字串,例如 "x.y.z"。
Log.d("MyApp", "SDK version: ${HappyOyster.VERSION}")
事件監聽
interface HappyOysterListener {
fun onStatusChanged(status: TravelStatusValue) {}
fun onError(error: SDKError) {}
}
fun addListener(listener: HappyOysterListener)
fun removeListener(listener: HappyOysterListener)
SDK 事件是對你的主動推送通道,用於回呼那些非你主動呼叫觸發的情況(例如 SDK 內部自動維護的即時連線或狀態輪詢出現問題)。
事件 | 說明 |
|---|---|
| 體驗狀態變更(包含即時視訊生命週期),值請參閱 |
| 內部自動流程發生錯誤時回呼;致命錯誤會同時終止本次體驗(請參閱錯誤碼章節)。 |
說明addListener / removeListener 必須在 initialize 之後呼叫;在初始化前呼叫會擲回 SDKError(100001)。建議在 HappyOyster.initialize(...) 成功返回後立即註冊監聽器。
資料模型
// 配置
data class SDKConfig(
// 必填:你账号的百炼 API Host(如 llm-xxxx.ap-southeast-1.maas.aliyuncs.com),百炼控制台 API Key 页复制;
// 须与注入的 API Key 同账号/区域,否则网关返回 AccessDenied。
val apiHost: String,
// 必填且无默认值:包含版本的完整 HappyOyster 模型名称(如 happyoyster-1.0-directing);
// 可用值以百炼官方 HappyOyster 系列模型文档为准。
val model: String,
// 内置 Logcat 输出的最低等级(不影响 logHandler);默认 INFO,含重建会话时间线所需的
// 生命周期锚点(初始化、Travel 起止/状态、RTC 入会/首帧)。仅需报错可降为 WARN,排障可升到 DEBUG/VERBOSE。
val logLevel: LogLevel = LogLevel.INFO,
// RTC 入会(超时触发 105002)、首帧等待(超时触发 105003)与 SDK 网关 HTTP 信令调用(超时触发 105005)的超时;
// 两个 RTC 阶段串行,最坏等待 2×callbackTimeoutMs(60s)。
val callbackTimeoutMs: Long = SDKConfig.DEFAULT_CALLBACK_TIMEOUT_MS, // 30_000ms
// SDK 是否将自身日志写入 Android Logcat(tag HappyOysterSDK);默认 false(静默)。
val logcatEnabled: Boolean = false,
// 宿主日志回调;接收全量 SDK LogRecord,不受 logLevel 影响;默认 null。
val logHandler: HappyOysterLogHandler? = null,
) {
companion object {
/** 默认回调超时(毫秒)。公开常量,可用于对比或显示。 */
const val DEFAULT_CALLBACK_TIMEOUT_MS: Long = 30_000
}
}
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }
// 宿主日志接收器;通过 SDKConfig.logHandler 注入;全量 firehose,不受 logLevel 影响。
fun interface HappyOysterLogHandler {
fun onLog(record: LogRecord)
}
// SDK 结构化日志记录;交付给 HappyOysterLogHandler;不含敏感值(Bearer / ticket / RTC token、
// RTC 标识与媒体 URL 均已脱敏)。travelId(encryptedTravelId)保留全值,可用于与服务端会话日志对账。
data class LogRecord(
val level: LogLevel,
val tag: String, // 固定为 "HappyOysterSDK"
val message: String, // 可读日志行(含事件名与详情)
val throwable: Throwable?,
val timestampMs: Long, // 发出时的 epoch 毫秒
)
// 状态与模式(保留未知值,便于服务扩展)
@JvmInline value class TravelStatusValue(val rawValue: String) {
companion object {
val Init = TravelStatusValue("init")
val Pending = TravelStatusValue("pending")
val Running = TravelStatusValue("running")
val Paused = TravelStatusValue("paused")
val Failed = TravelStatusValue("failed")
val Completed = TravelStatusValue("completed")
}
}
@JvmInline value class ModeValue(val rawValue: String) {
companion object {
val Adventure = ModeValue("adventure")
val Directing = ModeValue("directing")
val Acting = ModeValue("acting")
}
}
@JvmInline value class CreationModelValue(val rawValue: String) {
companion object {
val Simple = CreationModelValue("simple") // 默认;prompt/instruct 驱动,世界探索类世界归一化为此值
val ScriptList = CreationModelValue("scriptlist") // 结构化 ScriptList 世界;不支持 sendInstruct(抛 103002)
}
}
// startTravel 返回
data class StartTravelData(
val encryptedTravelId: String, // 后续控制接口的标识
val encryptedWorldId: String,
val mode: ModeValue, // adventure / directing / acting
val creationModel: CreationModelValue = CreationModelValue.Simple, // 世界创建模型;Simple(默认)为 instruct 驱动,ScriptList 不支持 sendInstruct
val playUrl: String?,
val firstFrame: String?, // 首帧图地址,可能异步产生
val bgmUrl: String?,
val version: String, // 世界版本标识;角色演绎的 version 为 actingV2
val aspectRatio: String? = null, // 仅角色演绎返回 9:16 / 16:9;其余模式 null。用于定播放器方向
val maxExperienceTimeSec: Int? = null, // 服务端回显的本次最大体验时长(秒);仅 adventure 非 null,directing / acting 为 null
)
// 控制接口参数与返回
data class AdventureCommand(
val translation: String, // 移动:前/左/后/右/斜向(W_A 等)/静止
val rotation: String, // 视角:上/下/左/右/斜向(Mouse_Up_Left 等)/无
val interaction: String, // 交互:跳跃/攻击/蹲下/冲刺/无
)
data class TravelStateData(val encryptedTravelId: String, val status: TravelStatusValue)
data class RewindTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val resumedAtSec: Double)
data class EndTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val endedAt: String, val durationSec: Int)
data class SendInstructData(val encryptedTravelId: String, val content: String, val accepted: Boolean)
// 错误(以 code 标识,原始信息见 raw)
// 注意:SDKError 不是 data class(无 copy()/解构),是普通 class。
class SDKError(val code: Int, val raw: Any? = null) : Exception("Happy Oyster SDK error: $code")
錯誤碼
錯誤以數字 code 識別。SDK 會透傳可由呼叫端處理的業務錯誤碼(常見為 4xxxxx / 5xxxxx),本機 SDK 錯誤碼為 1xxxxx。
業務錯誤碼(常見)
code | 含義 | 建議處理方式 |
|---|---|---|
| 參數非法(列舉非法等) | 檢查請求參數或 SDK 版本 |
|
| 讓伺服器端重新下發憑證 |
|
| 憑證一次性,重新下發 |
| World 不存在、已刪除或不屬於目前開發者(包含 | 重新選擇有效 World |
| 世界狀態非就緒 | 等世界就緒後再開始 |
| 輸入內容違規(內容安全審查);適用於 | 修改輸入內容後重試 |
| 目前規格未開通 | 不可按容量滿重試;請更換已開通規格或聯絡開通 |
| 容量配置暫不可用 | 稍後重試 |
| Travel 資源不存在或 ID 不屬於目前帳號 | 重新開始 Travel |
| 請求與目前資源狀態衝突 | 檢查 Travel 狀態 |
| 目前規格併發已滿 | 待現有工作階段結束後重試(請勿與 |
| 目前可用容量不足 | 稍後重試 |
| 推理資源分配/服務內部失敗 | 稍後重試 |
| 系統內部錯誤 | 稍後重試 / 回饋 |
用戶端本機錯誤碼
說明「是否為致命錯誤」專指是否觸發 SDK 自動終止本次體驗:
100001/100002/103xxx這類同步驗證錯誤只會拒絕/擲回該次呼叫(100002會直接導致初始化失敗),不會終止體驗。- SDK 自動終止本次體驗分為四類:① 內部輪詢讀到
failed;② 即時連線嚴重錯誤(105001/105002/105003);③ 無推流自動結束(105006);④ SDK 被遠端停用(108001)。
code | 含義 | 是否為致命錯誤 | 建議處理方式 |
|---|---|---|---|
| SDK 未初始化即呼叫 | 呼叫拒絕 | 先 |
| SDK 初始化時 | 初始化失敗 | 傳入非空白的完整模型名稱及版本後重新 |
| 未注入百煉閘道 API Key | 否 |
|
| 百煉閘道 Bearer API Key 已過期或被拒絕。注意:這是閘道 Bearer Key,不是一次性 ticket;ticket 層級的憑證錯誤由六位數伺服器 code 識別(如 | 否 | 重新取得 Bearer Key 後執行 |
| 目前無活躍體驗 | 呼叫拒絕 | 先 |
| 當前狀態不允許該操作(包含:狀態不符、 | 呼叫拒絕 | 檢查體驗狀態與模式;ScriptList 模式( |
| 模式不匹配:在即時導演或角色演繹下呼叫了 | 呼叫拒絕 | 檢查目前模式是否符合介面要求 |
| 併發呼叫 | 呼叫拒絕 | 等待 |
| 即時連線失敗 | 是 | 結束並重新開始 |
| 即時入會逾時 | 是 | 結束並重新開始 |
| 等待視訊首幀逾時 | 是 | 結束並重新開始 |
| 即時通道未就緒/傳送失敗(例如 DataChannel 連線中斷或即時通道異常) | 否 | 確認即時通道已就緒,於 |
| SDK 閘道 HTTP 信令呼叫在 | 否 | 建議重試,或調大 |
| 無推流自動結束:首訊框從未送達,或執行中推流中斷且逾時未恢復。SDK 會主動結束本次體驗 | 是 | 結束並重新開始 |
| 本機網路錯誤 | 否 | 可重試 |
| 回應解析失敗 | 否 | 使用者主動呼叫時拋給呼叫方;內部狀態輪詢時僅觸發 |
| 上游服務傳回無法識別的錯誤回應,SDK 已將原始資訊保留在 | 否 | 可重試;若持續出現,請結合 |
| SDK 被遠端停用(整體關閉或版本過低);原因位於 | 是(SDK 偵測到停用結果時會自動結束進行中的體驗;恢復後可重新開始) | 依據 |
致命 vs 非致命
嚴重錯誤:SDK 會自動終止本次體驗(斷開即時連線、釋放資源、呼叫 endTravel),並透過 onError 透出;宿主應清理本次體驗狀態並允許重新開始。共四類:
- ① 內部狀態輪詢讀取到體驗狀態為
failed(例如500001推理失敗) - ② 即時連線發生致命錯誤(
105001/105002/105003) - ③ 無推流自動結束(
105006:首訊框從未送達,或執行中推流中斷且逾時未恢復,SDK 主動結束) - ④ SDK 被遠端停用(
108001)
非嚴重錯誤:不終止體驗,僅透過 onError 透出;您可重新 updateToken 或等待服務 / 即時通道恢復後繼續。內部狀態輪詢自身的單次請求失敗(網路 106001、解析 106002、上游異常 106003、業務錯誤 5xxxxx)屬於此類——輪詢會在下一週期繼續,只有它讀到狀態 failed 才會結束體驗;驗證失敗 101001、sendCommand 節流 flush 的非同步傳送失敗 105004 同樣為非嚴重錯誤。註:SDK 不會自行向即時通道傳送任何保活訊息,因此空閒期不會出現 105004——該錯誤只可能由您主動呼叫 sendCommand 觸發。